123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443(* elpi: embedded lambda prolog interpreter *)(* license: GNU Lesser General Public License Version 2.1 or later *)(* ------------------------------------------------------------------------- *)openElpi_util.Util(* each path element is one of these 4 "constructors" *)letkConstant=0(* p, q *)letkPrimitive=1(* 3, "foo" *)letkVariable=2(* X1, _ *)letkOther=3(* Lam + internal tags for list-begin/end, input, output, list-tail-variable *)(* 4 constructors encoded as: kno, arity, arg_value *)letarity_bits=4letk_bits=2(** [encode k c a]:
- k : the "constuctor" (kConstant, kPrimitive, kVariable, kOther)
- c : the "data"
- a : the "arity"
*)letcell_size=Sys.int_sizeletk_lshift=cell_size-k_bitsletka_lshift=cell_size-k_bits-arity_bitsletk_mask=((1lslk_bits)-1)lslk_lshiftletarity_mask=(((1lslarity_bits)lslk_bits)-1)lslka_lshiftletdata_mask=(1lslka_lshift)-1letencode~k~arity~data=(klslk_lshift)lor(aritylslka_lshift)lor(datalanddata_mask)letk_ofn=(nlandk_mask)lsrk_lshiftletarity_ofn=letk=k_ofninifk==kConstantthen(nlandarity_mask)lsrka_lshiftelse0letdata_ofn=(nlanddata_mask)letmkConstant~safe~data~arity=letrc=encode~k:kConstant~data~arityinifsafe&&(absdata>data_mask||arity>=1lslarity_bits)thenanomaly(Printf.sprintf"Indexing at depth > 1 is unsupported since constant %d/%d is too large or wide"dataarity);rcletmkPrimitivec=encode~k:kPrimitive~data:(CData.hashclslk_bits)~arity:0letmkVariable=encode~k:kVariable~data:0~arity:0letmkAny=encode~k:kOther~data:0~arity:0(* each argument starts with its mode *)letmkInputMode=encode~k:kOther~data:1~arity:0letmkOutputMode=encode~k:kOther~data:2~arity:0letmkListTailVariable=encode~k:kOther~data:3~arity:0letmkListHead=encode~k:kOther~data:4~arity:0letmkListEnd=encode~k:kOther~data:5~arity:0letmkListNil=encode~k:kOther~data:6~arity:0letmkPathEnd=encode~k:kOther~data:7~arity:0letmkListTailVariableUnif=encode~k:kOther~data:8~arity:0letmkUvarVariable=encode~k:kVariable~data:9~arity:0letisVariablex=x==mkVariableletisAnyx=x==mkAnyletisInputx=x==mkInputModeletisOutputx=x==mkOutputModeletisListHeadx=x==mkListHeadletisListNilx=x==mkListNilletisListEndx=x==mkListEndletisListTailVariablex=x==mkListTailVariableletisListTailVariableUnifx=x==mkListTailVariableUnifletisPathEndx=x==mkPathEndletisUvarVariablex=x==mkUvarVariabletypecell=intletpp_cellfmtn=letk=k_ofninifk==kConstantthenletdata=data_masklandninletarity=(arity_masklandn)lsrka_lshiftinFormat.fprintffmt"Constant(%d,%d)"dataarityelseifk==kVariablethenFormat.fprintffmt"Variable"elseifk==kOtherthenFormat.fprintffmt(ifisInputnthen"Input"elseifisOutputnthen"Output"elseifisListTailVariablenthen"ListTailVariable"elseifisListHeadnthen"ListHead"elseifisListEndnthen"ListEnd"elseifisPathEndnthen"PathEnd"elseifisListNilnthen"ListNil"elseifisListTailVariableUnifnthen"ListTailVariableUnif"elseifisAnynthen"Other"elseifisUvarVariablenthen"uvar"elsefailwith"Invalid path construct...")elseifk==kPrimitivethenFormat.fprintffmt"Primitive"elseFormat.fprintffmt"%o"kletshow_celln=Format.asprintf"%a"pp_cellnmodulePath=structtypet=cellarray[@@derivingshow]letgetai=a.(i)typebuilder={mutablepos:int;mutablepath:cellarray}letget_builder_pos{pos}=posletmakesizee={pos=0;path=Array.makesizee}letrecemitpe=letlen=Array.lengthp.pathinifp.pos<len-1thenbeginArray.unsafe_setp.pathp.pose;p.pos<-p.pos+1endelsebeginletnewpath=Array.make(2*len)mkPathEndinArray.blitp.path0newpath0len;p.path<-newpath;emitpeendletstop{path}=pathletof_listl=letpath=make1(List.hdl)inList.iter(emitpath)l;stoppathendmoduleTrie=struct(*
* Trie: maps over lists.
* Note: This code is a heavily modified version of the original code.
* Copyright (C) 2000 Jean-Christophe FILLIATRE
*
* This software is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2, as published by the Free Software Foundation.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU Library General Public License version 2 for more details
* (enclosed in the file LGPL).
*)(*s A trie is a tree-like structure to implement dictionaries over
keys which have list-like structures. The idea is that each node
branches on an element of the list and stores the value associated
to the path from the root, if any. Therefore, a trie can be
defined as soon as a map over the elements of the list is
given. *)(*s Then a trie is just a tree-like structure, where a possible
information is stored at the node (['a option]) and where the sons
are given by a map from type [key] to sub-tries, so of type
['a t Ptmap.t]. The empty trie is just the empty map. *)type'at=Nodeof{data:'alist;(* children *)other:'atoption;(* for Other, Variable *)listTailVariable:'atoption;(* for ListTailVariable *)map:'atPtmap.t;(* for Constant, Primitive, ListHead, ListEnd *)}letempty=Node{data=[];other=None;listTailVariable=None;map=Ptmap.empty}letis_emptyx=x==emptyletrecreplacepx=function|Node{data;other;listTailVariable;map}->Node{data=data|>List.map(funy->ifpythenxelsey);other=other|>Option.map(replacepx);listTailVariable=listTailVariable|>Option.map(replacepx);map=map|>Ptmap.map(replacepx);}letrecremovef=function|Node{data;other;listTailVariable;map}->Node{data=data|>List.filter(funx->not(fx));other=other|>Option.map(removef);listTailVariable=listTailVariable|>Option.map(removef);map=map|>Ptmap.map(removef);}letadd(a:Path.t)vt=letmax=ref0inletrecins~pos=letx=Path.getaposinfunction|Node({data}ast)whenisPathEndx->max:=pos;Node{twithdata=v::data}|Node({other}ast)whenisVariablex||isAnyx->lett'=matchotherwithNone->empty|Somex->xinlett''=ins~pos:(pos+1)t'inNode{twithother=Somet''}|Node({listTailVariable}ast)whenisListTailVariablex||isListTailVariableUnifx->lett'=matchlistTailVariablewithNone->empty|Somex->xinlett''=ins~pos:(pos+1)t'inNode{twithlistTailVariable=Somet''}|Node({map}ast)->lett'=tryPtmap.findxmapwithNot_found->emptyinlett''=ins~pos:(pos+1)t'inNode{twithmap=Ptmap.addxt''map}inlett=ins~pos:0tint,!maxletrecpp(ppelem:Format.formatter->'a->unit)(fmt:Format.formatter)(Node{data;other;listTailVariable;map}:'at):unit=Format.fprintffmt"@[<v>[@[<hov 2>values:{@ ";pplistppelem"; "fmtdata;Format.fprintffmt"}@ @]@ @[<hov 2> other:{@ ";(matchotherwithNone->()|Somem->ppppelemfmtm);Format.fprintffmt"}@ @]@ @[<hov 2> listTailVariable:{@ ";(matchlistTailVariablewithNone->()|Somem->ppppelemfmtm);Format.fprintffmt"}@ @]@ @[<hov 2> key:{@ @[<hov 2>";Ptmap.to_listmap|>pplist(funfmt(k,v)->Format.fprintffmt"@[<hov 2> %a@ %a@]"pp_cellk(ppppelem)v)";@ "fmt;Format.fprintffmt"@]}@]]@]"letshow(fmt:Format.formatter->'a->unit)(n:'at):string=letb=Buffer.create22inFormat.fprintf(Format.formatter_of_bufferb)"@[%a@]"(ppfmt)n;Buffer.contentsbendletupdate_par_countnk=ifisListHeadkthenn+1elseifisListEndk||isListTailVariablek||isListTailVariableUnifkthenn-1elsenletskip~pospath(*hd tl*):int=letrecaux_listaccp=ifacc=0thenpelseaux_list(update_par_countacc(Path.getpathp))(p+1)inletrecaux_constarityp=ifarity=0thenpelseifisListHead(Path.getpathp)thenletskip_list=aux_list1(p+1)inaux_const(arity-1)skip_listelseaux_const(arity-1+arity_of(Path.getpathp))(p+1)inifisListHead(Path.getpathpos)thenaux_list1(pos+1)elseaux_const(arity_of(Path.getpathpos))(pos+1)(**
Takes a path and skip a listTailVariable:
we take the node just after the first occurrence of isListEnd or isListTailVariable
with no corresponding isListHead
*)letskip_listTailVariable~pospath:int=letrecauxipc=ifpc=0thenielseaux(i+1)(update_par_countpc(Path.getpathi))inaux(pos+1)(update_par_count1(Path.getpathpos))(* A discrimination tree is a record {t; max_size; max_depths; max_list_length; user_upper_bound_depth } where
- t is a Trie for instance retrival
- max_size is a int representing the max size of a path in the Trie
- max_depths is a int list representing the max depth of the each indexed arguments
- max_list_length is the length of the maximum indexed list
- user_upper_bound_depth is the index defined by the user
Invariants:
- length(max_depths) = length(user_upper_bound_depth)
- forall i in range (length max_depths):
assert (max_depths[i] <= user_upper_bound_depth[i])
Note :
- max_size is used as an heuristic for the size of path when a new one is created
(we use arrays, therefore we have to allocate them)
- During the construction of a goal's path using max_depths, the goal's argument can
be larger than that of the corresponding instances. If the indexing depth
for an argument i is set to X by the user but the maximal depth of i in the
database is Y (where Y is much less than X it is inefficient to build a path
of height X for the goal. Instead, constructing a path of height Y is more
efficient and can lead to performance gains. As an example, consider the following:
```elpi
kind term type.
type app list term -> term.
type con string -> term.
:index (100)
pred p i:term.
p (app[con "f", app[X]]).
main :-
p (app [con "f", app[app[app[...app[con "g"]]]]])
```
In the example it is no needed to index the goal path to depth 100, but rather considering
the maximal depth of the first argument, which 4 << 100
*)type'at={t:'aTrie.t;max_size:int;max_depths:intarray;max_list_length:int;user_upper_bound_depth:intlist}letpppp_afmt{t}:unit=Trie.pp(funfmtdata->pp_afmtdata)fmttletshowpp_a{t}:string=Trie.show(funfmtdata->pp_afmtdata)tletindex{t;max_size;max_depths;max_list_length=mll;user_upper_bound_depth}~max_list_lengthpathdata=lett,(m:int)=Trie.addpathdatatin{t;max_size=maxmax_sizem;max_depths;max_list_length=maxmax_list_lengthmll;user_upper_bound_depth}letmax_path{max_size}=max_sizeletmax_depths{max_depths}=max_depthsletmax_list_length{max_list_length}=max_list_lengthletuser_upper_bound_deptht=t.user_upper_bound_depth(* the equivalent of skip, but on the index, thus the list of trees
that are rooted just after the term represented by the tree root
are returned (we are skipping the root) *)letcallf=letres=ref@@[]inletadd_resultx=res:=x::!resinf~add_result;!resletskip_to_listEnd~add_resultmode(Trie.Node{other;map;listTailVariable})=letrecget_from_listn=function|Trie.Node{other=None;map;listTailVariable}astree->ifn=0thenadd_resulttreeelse(Ptmap.iter(funkv->get_from_list(update_par_countnk)v)map;matchlistTailVariablewithNone->()|Somea->add_resulta)|Trie.Node{other=Someother;map;listTailVariable}astree->ifn=0then(add_resulttree;add_resultother)else(get_from_listnother;Ptmap.iter(funkv->get_from_list(update_par_countnk)v)map;matchlistTailVariablewithNone->()|Somea->add_resulta)inletsome_to_list=functionSomex->add_resultx|None->()in(matchotherwithNone->()|Somea->get_from_list1a);ifisOutputmodethenPtmap.iter(funkv->get_from_list(update_par_count1k)v)map;some_to_listlistTailVariableletskip_to_listEndmodet=call(skip_to_listEndmodet)letget_all_childrenvmode=isAnyv||(isVariablev&&isOutputmode)letrecretrieve~pos~add_resultmodepathtree:unit=lethd=Path.getpathposinletTrie.Node{data;map;other;listTailVariable}=treein(* Format.eprintf "%d %a\n%!" pos pp_cell hd; *)ifTrie.is_emptytreethen()elseifisPathEndhdthenList.iteradd_resultdataelseifisInputhd||isOutputhdthen(* next argument, we update the mode *)retrieve~pos:(pos+1)~add_resulthdpathtreeelseifisListTailVariablehd||isListTailVariableUnifhdthenletsub_tries=skip_to_listEnd(ifisListTailVariableUnifhdthenmkOutputModeelsemode)treeinList.iter(retrieve~pos:(pos+1)~add_resultmodepath)sub_trieselsebegin(* Here the constructor can be Constant, Primitive, Variable, Other, ListHead, ListNil, ListEnd *)beginifget_all_childrenhdmodethen(* we take all the children in the map *)on_all_children~pos:(pos+1)~add_resultmodepathmapelseifisInputmode&&isVariablehdthentryretrieve~pos:(pos+1)~add_resultmodepath(Ptmap.findmkUvarVariablemap)withNot_found->()else(* we have a Constant, Primitive, ListHead, ListNil or ListEnd and look for the key in the map *)tryretrieve~pos:(pos+1)~add_resultmodepath(Ptmap.findhdmap)withNot_found->()end;(* moreover, we have to take into account other and listTailVariable since they represent UVar in the tree,
therefore they can be unified with the hd *)ifnot(isListEndhd)thenOption.iter(funa->retrieve~pos:(skip~pospath)~add_resultmodepatha)other;Option.iter(funa->retrieve~pos:(skip_listTailVariable~pospath)~add_resultmodepatha)listTailVariableendandon_all_children~pos~add_resultmodepathmap=letrecskip_listpar_countarity=function|Trie.Node{other;map;listTailVariable}astree->ifpar_count=0thenbeginskip_functoraritytreeendelsebeginPtmap.iter(funkv->skip_list(update_par_countpar_countk)arityv)map;Option.iter(skip_listpar_countarity)other;Option.iter(skip_list(par_count-1)arity)listTailVariableendandskip_functorarity=function|Trie.Node{other;map}astree->ifarity=0thenretrieve~pos~add_resultmodepathtreeelsebeginOption.iter(skip_functor(arity-1))other;Ptmap.iter(funkv->ifisListHeadkthenskip_list1(arity-1)velseskip_functor(arity-1+arity_ofk)v)mapendinPtmap.iter(funkv->ifisListHeadkthenskip_list10velseskip_functor(arity_ofk)v)mapletempty_dtargs_depth:'at=letmax_depths=Array.make(List.lengthargs_depth)0in{t=Trie.empty;max_depths;max_size=0;max_list_length=0;user_upper_bound_depth=args_depth}letretrieve~pos~add_resultpathindex=letmode=Path.getpathposinassert(isInputmode||isOutputmode);retrieve~add_resultmode~pos:(pos+1)pathindexletretrievecmp_datapath{t}=letr=call(retrieve~pos:0patht)inBl.of_list@@List.sortcmp_datarletreplacepxi={iwitht=Trie.replacepxi.t}letremovekeepdt={dtwitht=Trie.removekeepdt.t}moduleInternal=structletkConstant=kConstantletkPrimitive=kPrimitiveletkVariable=kVariableletkOther=kOtherletk_of=k_ofletarity_of=arity_ofletdata_of=data_ofletisVariable=isVariableletisAny=isAnyletisInput=isInputletisOutput=isOutputletisListHead=isListHeadletisListEnd=isListEndletisListTailVariable=isListTailVariableletisListTailVariableUnif=isListTailVariableUnifletisPathEnd=isPathEndletisUvarVariable=isUvarVariableletisListNil=isListNilend