Skip to content

Commit 4ce8dea

Browse files
authored
Indexing via discrimination tree (#205)
1 parent c18e9d1 commit 4ce8dea

20 files changed

+599
-104
lines changed

.github/workflows/main.yml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,12 @@ jobs:
8383
opam exec -- cygpath -m ${{ github.workspace }} | % {$_ -replace "^","workspace=" } | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
8484
opam exec -- cygpath -m "$Env:CYGWIN_ROOT" | % {$_ -replace "^","cygwin_root=" } | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
8585
opam exec -- sed -i ' ' tests/sources/*.elpi
86-
& "$Env:CYGWIN_ROOT/setup-x86_64.exe" -q -P time
87-
& "$Env:CYGWIN_ROOT/setup-x86_64.exe" -q -P which
88-
& "$Env:CYGWIN_ROOT/setup-x86_64.exe" -q -P wdiff
89-
opam exec -- which which
90-
opam exec -- time which
91-
opam exec -- which time
92-
opam exec -- which wdiff
86+
& "$Env:CYGWIN_ROOT/setup-x86_64.exe" -v -q -P time,which,wdiff
87+
88+
# opam exec -- which which
89+
# opam exec -- time which
90+
# opam exec -- which time
91+
# opam exec -- which wdiff
9392

9493
# Build ######################################################################
9594
#

.ocamlformat

Whitespace-only changes.

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ API:
77
Library:
88
- New `std.fold-right`
99

10+
Runtime:
11+
- New clause retrieval through discrimination tree. This new index is enabled
12+
whenever the `:index` directive selects only one argument with a depth `> 1`.
13+
1014
# v1.18.0 (October 2023)
1115

1216
Requires Menhir 20211230 and OCaml 4.08 or above.

ELPI.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ trivial-facts :-
6666
```
6767

6868
Side note: no solution is computed for goals like `_ = something`.
69-
On the contrary a problem like `DummyNameUsedOnlyOnce = somthing` demands the
69+
On the contrary a problem like `DummyNameUsedOnlyOnce = something` demands the
7070
computation of the solution (even if it is not used), and hence can *fail* if
7171
some variable occurring in something is out of scope for `DummyNameUsedOnlyOnce`.
7272

@@ -308,8 +308,20 @@ If only one argument is indexed, and it is indexed at depth one, then Elpi uses
308308
a standard indexing technique based on a perfect (for depth 1) search tree. This
309309
means that no false positives are returned by the index.
310310

311-
If more than one argument is indexed, or if some argument is indexed at depth
312-
greater than 1, then Elpi uses an index based on the idea of
311+
### Discrimination tree index
312+
313+
If only one argument is indexed at depth greater than one, then Elpi uses
314+
a [discrimination tree](https://www.cs.cmu.edu/~fp/courses/99-atp/lectures/lecture28.html).
315+
Both the rule argument and the goal argument are
316+
indexed up to the given depth. The indexing is not perfect, false positives may
317+
be returned and ruled out by unification.
318+
319+
Indexed terms are linearized into paths. Paths are inserted into a trie data
320+
structure sharing common prefixes.
321+
322+
### Hash based index
323+
324+
If more than one argument is indexed then Elpi uses an index based on the idea of
313325
[unification hashes](http://blog.ndrix.com/2013/03/prolog-unification-hashes.html).
314326

315327
```prolog
@@ -618,7 +630,7 @@ syntax `{{:name` .. `}}` where `name` is any non-space or `\n` character.
618630
Quotations are elaborated before run-time.
619631

620632
The [coq-elpi](https://github.com/LPCIC/coq-elpi) software embeds elpi
621-
in Coq and provides a quatation for its terms. For example
633+
in Coq and provides a quotation for its terms. For example
622634
```prolog
623635
{{ nat -> bool }}
624636
```

INCOMPATIBILITIES.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Known incompatibilities with Teyjus
22
===================================
33

4-
This file tries to summarise known incompatibilities between Elpi and Teyjus.
4+
This file tries to summarize known incompatibilities between Elpi and Teyjus.
55

66
# Semantics
77

@@ -37,7 +37,7 @@ This file tries to summarise known incompatibilities between Elpi and Teyjus.
3737
- Module signatures are ignored.
3838
- Elpi accumulates each file once; Teyjus does it multiple times, that is
3939
always bad (all clauses are duplicated and tried multiple times, that is
40-
rarely the expected behaviour).
40+
rarely the expected behavior).
4141
- Elpi understands relative paths as in `accumulate "../foo"`: resolution
4242
of relative paths is done according to the path of the accumulating file
4343
first or, if it fails, according to the TJPATH.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ The elaborator manipulates terms with binders and holes
134134
(unification variables) representing missing piece of
135135
information. Some of them have to be filled in order
136136
to make the term well typed. Some others are filled in because
137-
the user has programmed the eleborator to do so, e.g. ad-hoc polymorphism.
137+
the user has programmed the elaborator to do so, e.g. ad-hoc polymorphism.
138138

139139
Such software component is characterized by an high complexity
140140
coming from the interplay of binders, reduction and unification,

src/compiler.ml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,7 @@ let query_preterm_of_ast ~depth macros state (loc, t) =
12331233
Loc.show (snd (C.Map.find name map)) ^ ")")
12341234

12351235
let compile_mode (state, modes) { Ast.Mode.name; args; loc } =
1236+
let args = List.map to_mode args in
12361237
let state, mname = funct_of_ast state name in
12371238
check_duplicate_mode state mname (args,loc) modes;
12381239
state, C.Map.add mname (args,loc) modes
@@ -1356,7 +1357,7 @@ let query_preterm_of_ast ~depth macros state (loc, t) =
13561357
(state : State.t), lcs, active_macros,
13571358
{ Structured.types; type_abbrevs; modes; body; symbols }
13581359

1359-
and compile_body macros types type_abbrevs modes lcs defs state = function
1360+
and compile_body macros types type_abbrevs (modes : (mode * Loc.t) C.Map.t) lcs defs state = function
13601361
| [] -> lcs, state, types, type_abbrevs, modes, defs, []
13611362
| Locals (nlist, p) :: rest ->
13621363
let orig_varmap = get_varmap state in
@@ -1673,11 +1674,11 @@ module Spill : sig
16731674

16741675

16751676
val spill_clause :
1676-
State.t -> types:Structured.typ list C.Map.t -> modes:(constant -> bool list) ->
1677+
State.t -> types:Structured.typ list C.Map.t -> modes:(constant -> mode) ->
16771678
(preterm, 'a) Ast.Clause.t -> (preterm, 'a) Ast.Clause.t
16781679

16791680
val spill_chr :
1680-
State.t -> types:Structured.typ list C.Map.t -> modes:(constant -> bool list) ->
1681+
State.t -> types:Structured.typ list C.Map.t -> modes:(constant -> mode) ->
16811682
(constant list * prechr_rule list) -> (constant list * prechr_rule list)
16821683

16831684
(* Exported to compile the query *)
@@ -1727,7 +1728,7 @@ end = struct (* {{{ *)
17271728
| `Arrow(arity,_),_ ->
17281729
let missing = arity - nargs in
17291730
let output_suffix =
1730-
let rec aux = function false :: l -> 1 + aux l | _ -> 0 in
1731+
let rec aux = function Output :: l -> 1 + aux l | _ -> 0 in
17311732
aux (List.rev mode) in
17321733
if missing > output_suffix then
17331734
error ~loc Printf.(sprintf
@@ -2358,14 +2359,13 @@ let compile_clause modes initial_depth state
23582359
state, cl
23592360

23602361
let chose_indexing state predicate l =
2361-
let rec all_zero = function
2362-
| [] -> true
2363-
| 0 :: l -> all_zero l
2364-
| _ -> false in
2365-
let rec aux n = function
2362+
let all_zero = List.for_all ((=) 0) in
2363+
let rec aux argno = function
2364+
(* TODO: @FissoreD here we should raise an error if n > arity of the predicate? *)
23662365
| [] -> error ("Wrong indexing for " ^ Symbols.show state predicate)
2367-
| 0 :: l -> aux (n+1) l
2368-
| 1 :: l when all_zero l -> MapOn n
2366+
| 0 :: l -> aux (argno+1) l
2367+
| 1 :: l when all_zero l -> MapOn argno
2368+
| path_depth :: l when all_zero l -> Trie { argno ; path_depth }
23692369
| _ -> Hash l
23702370
in
23712371
aux 0 l

src/data.ml

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ module Constants : sig
5353
module Set : Set.S with type elt = constant
5454
val pp : Format.formatter -> t -> unit
5555
val show : t -> string
56+
val compare : t -> t -> int
5657
end = struct
5758

5859
module Self = struct
@@ -115,6 +116,25 @@ let uvar_isnt_a_blocker { uid_private } = uid_private > 0 [@@inline];;
115116
let uvar_set_blocker r = r.uid_private <- -(uvar_id r) [@@inline];;
116117
let uvar_unset_blocker r = r.uid_private <- (uvar_id r) [@@inline];;
117118

119+
type arg_mode = Util.arg_mode = Input | Output [@@deriving show]
120+
121+
type clause = {
122+
depth : int;
123+
args : term list;
124+
hyps : term list;
125+
vars : int;
126+
mode : mode; (* CACHE to avoid allocation in get_clauses *)
127+
loc : Loc.t option; (* debug *)
128+
}
129+
and
130+
mode = arg_mode list
131+
[@@deriving show]
132+
133+
let to_mode = function true -> Input | false -> Output
134+
135+
136+
module DT = Discrimination_tree
137+
118138
type stuck_goal = {
119139
mutable blockers : blockers;
120140
kind : unification_def stuck_goal_kind;
@@ -138,25 +158,23 @@ and second_lvl_idx =
138158
| TwoLevelIndex of {
139159
mode : mode;
140160
argno : int;
141-
all_clauses : clause list; (* when the query is flexible *)
142-
flex_arg_clauses : clause list; (* when the query is rigid but arg_id ha nothing *)
143-
arg_idx : clause list Ptmap.t; (* when the query is rigid (includes in each binding flex_arg_clauses) *)
161+
all_clauses : clause list; (* when the query is flexible *)
162+
flex_arg_clauses : clause list; (* when the query is rigid but arg_id ha nothing *)
163+
arg_idx : clause list Ptmap.t; (* when the query is rigid (includes in each binding flex_arg_clauses) *)
144164
}
145165
| BitHash of {
146166
mode : mode;
147167
args : int list;
148-
time : int; (* time is used to recover the total order *)
168+
time : int; (* time is used to recover the total order *)
149169
args_idx : (clause * int) list Ptmap.t; (* clause, insertion time *)
150170
}
151-
and clause = {
152-
depth : int;
153-
args : term list;
154-
hyps : term list;
155-
vars : int;
156-
mode : mode; (* CACHE to avoid allocation in get_clauses *)
157-
loc : Loc.t option; (* debug *)
171+
| IndexWithTrie of {
172+
mode : mode;
173+
argno : int; (* position of argument on which the trie is built *)
174+
path_depth : int; (* depth bound at which the term is inspected *)
175+
time : int; (* time is used to recover the total order *)
176+
args_idx : clause DT.t;
158177
}
159-
and mode = bool list (* true=input, false=output *)
160178
[@@deriving show]
161179

162180
type constraints = stuck_goal list
@@ -167,9 +185,19 @@ type suspended_goal = {
167185
goal : int * term
168186
}
169187

188+
(**
189+
Used to index the parameters of a predicate P
190+
- [MapOn N] -> N-th argument at depth 1 (head symbol only)
191+
- [Hash L] -> L is the list of depths given by the urer for the parameters of
192+
P. Indexing is done by hashing all the parameters with a non
193+
zero depth and comparing it with the hashing of the parameters
194+
of the query
195+
- [IndexWithTrie N D] -> N-th argument at D depth
196+
*)
170197
type indexing =
171198
| MapOn of int
172199
| Hash of int list
200+
| Trie of { argno : int; path_depth : int }
173201
[@@deriving show]
174202

175203
let mkLam x = Lam x [@@inline]

0 commit comments

Comments
 (0)