Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions datalog.opam
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ bug-reports: "https://github.com/c-cube/datalog/issues"
depends: [
"dune" {>= "2.7" & >= "2.7"}
"ocaml" {>= "4.08"}
"containers" {>= "2.2"}
"odoc" {with-doc}
"mdx" {>= "1.3" & with-test}
]
Expand Down
1 change: 1 addition & 0 deletions dune-project
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
(depends
(dune ( >= "2.7" ))
(ocaml ( >= "4.08" ))
(containers ( >= "2.2" ))
(odoc :with-doc)
(mdx (and ( >= "1.3" ) :with-test))))
47 changes: 45 additions & 2 deletions src/top_down/Datalog_top_down.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1465,6 +1465,8 @@ module Make(Const : CONST) = struct
mutable poss : (goal_entry * C.t) list; (* positive waiters *)
mutable negs : (goal_entry * C.t) list; (* negative waiters *)
mutable complete : bool; (* goal evaluation completed? *)
dep_goals : goal_entry T.Tbl.t ; (* forward edges: positive subgoals *)
mutable exit_fired : bool; (* exit frame processed, pending completion (idempotence) *)
} (** Root of the proof forest *)

(** In a goal entry, [poss] and [negs] are other goals that depend
Expand All @@ -1487,6 +1489,14 @@ module Make(Const : CONST) = struct
Subst.reset_renaming query.renaming;
query.renaming

let[@inline] get_dep_goals (g:goal_entry) : _ T.Tbl.t = g.dep_goals

let all_deps_complete (g:goal_entry) : bool =
try
T.Tbl.iter (fun _ dep -> if not dep.complete then raise_notrace Exit) g.dep_goals;
true
with Exit -> false

(* try to resolve fact with clause's first body literal *)
let resolve ~query fact clause =
match clause.C.body with
Expand Down Expand Up @@ -1522,7 +1532,7 @@ module Make(Const : CONST) = struct
query.stack <- stack';
(* close goal entry *)
if not goal_entry.complete
then slg_complete ~query goal_entry;
then slg_exit ~query goal_entry;
slg_main ~query
| NewClause (goal_entry, clause, stack') ->
query.stack <- stack';
Expand All @@ -1546,10 +1556,12 @@ module Make(Const : CONST) = struct
(* new goal! insert it in the forest, and start solving it *)
let goal_entry = {
goal;
answers = T.Tbl.create 7;
answers = T.Tbl.create 8;
poss = [];
negs = [];
complete = false;
dep_goals = T.Tbl.create 4;
exit_fired = false;
} in
TVariantTbl.add query.forest goal goal_entry;
(* push the goal on stack so that it is solved *)
Expand Down Expand Up @@ -1631,6 +1643,12 @@ module Make(Const : CONST) = struct
let subgoal_entry = slg_solve ~query subgoal in
(* register for future answers *)
subgoal_entry.poss <- (goal_entry, clause) :: subgoal_entry.poss;
(* record this as a forward dep if subgoal is still incomplete *)
if not subgoal_entry.complete
&& not (T.Tbl.mem (get_dep_goals goal_entry) subgoal) then (
let tbl = get_dep_goals goal_entry in
T.Tbl.replace tbl subgoal subgoal_entry;
);
(* use current answers *)
T.Tbl.iter
(fun ans () -> match resolve ~query ans clause with
Expand Down Expand Up @@ -1724,6 +1742,19 @@ module Make(Const : CONST) = struct
groups;
()

(* Exit fires when local clause set is exhausted.
Defer actual completion until all positive deps are done. *)
and slg_exit ~query goal_entry =
goal_entry.exit_fired <- true;
try_complete ~query goal_entry

(* Complete only when Exit has fired AND all dep_goals are complete *)
and try_complete ~query goal_entry =
if goal_entry.exit_fired
&& not goal_entry.complete
&& all_deps_complete goal_entry
then slg_complete ~query goal_entry

(* goal is completely evaluated, no more answers will arrive. *)
and slg_complete ~query goal_entry =
_debug (fun k->k "slg_complete %a" T.fmt goal_entry.goal);
Expand All @@ -1736,9 +1767,21 @@ module Make(Const : CONST) = struct
(fun (goal, clause) -> slg_newclause ~query goal clause)
goal_entry.negs
end;
(* Notify distinct waiters: decrement their dep count, maybe unblock them *)
let seen = T.Tbl.create 4 in
List.iter (fun (waiter, _) ->
if not (T.Tbl.mem seen waiter.goal) then begin
T.Tbl.add seen waiter.goal ();
(* Remove completed dep from waiter's dep_goals *)

T.Tbl.remove waiter.dep_goals waiter.goal;
try_complete ~query waiter
end
) goal_entry.poss;
(* reclaim memory *)
goal_entry.negs <- [];
goal_entry.poss <- [];
T.Tbl.clear goal_entry.dep_goals;
()
end

Expand Down
2 changes: 1 addition & 1 deletion src/top_down/dune
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(library
(name datalog_top_down)
(public_name datalog.top_down)
(libraries datalog)
(libraries datalog containers.scc)
(flags :standard -color always -safe-string -warn-error -a+8))

(ocamllex
Expand Down
11 changes: 11 additions & 0 deletions tests/clique10.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(7).
(9).
(4).
(8).
(10).
(2).
(3).
(5).
(6).
(1).
(0).
56 changes: 56 additions & 0 deletions tests/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@

(rule
(alias runtest)
(deps
(:file reg13.pl)
(:bin ../src/top_down_cli/topDownCli.exe))
(action
(progn
(with-stdout-to reg13.out
(run %{bin} -load %{file} "(X) :- result(a3, X)"))
(diff reg13.out reg13.expected))))

(rule
(alias runtest)
(deps
(:file clique10.pl)
(:bin ../src/top_down_cli/topDownCli.exe))
(action
(progn
(with-stdout-to clique10.out
(run %{bin} -load %{file} "(X) :- reachable(0,X)"))
(diff clique10.out clique10.expected))))

(rule
(alias runtest)
(deps
(:file graph10.pl)
(:bin ../src/top_down_cli/topDownCli.exe))
(action
(progn
(with-stdout-to graph10.out
(run %{bin} -load %{file} "(X) :- increasing(3,X)" -builtin))
(diff graph10.out graph10.expected))))

(rule
(alias runtest)
(deps
(:file small.pl)
(:bin ../src/top_down_cli/topDownCli.exe))
(action
(progn
(with-stdout-to small.out
(run %{bin} -load %{file} "(X) :- ancestor(X,john)"))
(diff small.out small.expected))))

(rule
(alias runtest)
(deps
(:file ship.pl)
(:bin ../src/top_down_cli/topDownCli.exe))
(action
(progn
(with-stdout-to ship.out
(run %{bin} -load %{file} "(P,C) :- ship_to(P,C)"))
(diff ship.out ship.expected))))

7 changes: 7 additions & 0 deletions tests/graph10.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
(7).
(9).
(4).
(8).
(10).
(6).
(5).
1 change: 1 addition & 0 deletions tests/reg13.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(a2).
13 changes: 13 additions & 0 deletions tests/reg13.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
% Test case for issue #13: Top down query has incorrect/differing behavior
% https://github.com/c-cube/datalog/issues/13

foo(a1, a3).
foo(a2, a3).

fooAlso(X, Y) :- foo(X, Y).

bar(a1, a2).

baz(a3, X) :- fooAlso(X, a3), fooAlso(Y, a3), bar(X, Y).

result(a3, X) :- fooAlso(X, a3), ~baz(a3, X).
5 changes: 5 additions & 0 deletions tests/ship.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(flowers, San Francisco).
(sausage, munich).
(horse, seoul).
(bread, paris).
(tea, london).
4 changes: 4 additions & 0 deletions tests/small.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(jean-jacques).
(mireille).
(alphonse).
(brad).