Skip to content

Commit e90d03f

Browse files
pmderodatraph-amiard
authored andcommitted
Symbolic solver: add a timeout feature
TN: SB20-024
1 parent 0019e17 commit e90d03f

9 files changed

+608
-75
lines changed

support/langkit_support-adalog-generic_main_support.adb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ package body Langkit_Support.Adalog.Generic_Main_Support is
7171
-- Solve_All --
7272
---------------
7373

74-
procedure Solve_All (Rel : Relation) is
74+
procedure Solve_All (Rel : Relation; Timeout : Natural := 0) is
7575

7676
type Var_And_Val is record
7777
Var : Refs.Logic_Var;
@@ -203,10 +203,12 @@ package body Langkit_Support.Adalog.Generic_Main_Support is
203203

204204
procedure Run_Solve (Opts : Solve_Options_Type) is
205205
begin
206-
Solve (Rel, Solution_Callback'Access, Opts);
206+
Solve (Rel, Solution_Callback'Access, Opts, Timeout);
207207
exception
208208
when Early_Binding_Error =>
209209
Put_Line ("Resolution failed with Early_Binding_Error");
210+
when Timeout_Error =>
211+
Put_Line ("Resolution failed with Timeout_Error");
210212
when Exc : others =>
211213
Put_Line (" -> " & Exception_Name (Exc)
212214
& ": " & Exception_Message (Exc));

support/langkit_support-adalog-generic_main_support.ads

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ package Langkit_Support.Adalog.Generic_Main_Support is
113113
function Logic_False return Relation is (+Create_False);
114114
function Logic_True return Relation is (+Create_True);
115115

116-
procedure Solve_All (Rel : Relation);
116+
procedure Solve_All (Rel : Relation; Timeout : Natural := 0);
117117

118118
procedure Run_Main (Main : access procedure);
119119
procedure Setup_Traces;

support/langkit_support-adalog-solver.adb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,12 @@ package body Langkit_Support.Adalog.Solver is
310310
Timeout : Natural := 0) is
311311
begin
312312
case Self.Kind is
313-
when Symbolic => Sym_Solve.Solve
314-
(Self.Symbolic_Relation, Solution_Callback, Solve_Options);
313+
when Symbolic =>
314+
Sym_Solve.Solve
315+
(Self.Symbolic_Relation,
316+
Solution_Callback,
317+
Solve_Options,
318+
Timeout);
315319
when State_Machine =>
316320
while Abstract_Relation.Solve (Self.SSM_Relation, Timeout) loop
317321
declare
@@ -354,7 +358,7 @@ package body Langkit_Support.Adalog.Solver is
354358
case Self.Kind is
355359
when Symbolic =>
356360
return Sym_Solve.Solve_First
357-
(Self.Symbolic_Relation, Solve_Options);
361+
(Self.Symbolic_Relation, Solve_Options, Timeout);
358362
when State_Machine =>
359363
return Abstract_Relation.Solve (Self.SSM_Relation, Timeout);
360364
when None => raise Constraint_Error with "Cannot solve No_Relation";

support/langkit_support-adalog-solver.ads

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,10 @@ package Langkit_Support.Adalog.Solver is
7979
-- process.
8080
--
8181
-- For the state machine solver, ``Timeout`` determines the maximum number
82-
-- of solving step to run before aborting the solver. If left to 0 or for
83-
-- the symbolic solver, no timeout applies.
82+
-- of solving step to run before aborting the solver. For the symbolic
83+
-- solver, ``Timeout`` determines the maximum of times we evaluate atoms
84+
-- before aborting the solver. In both cases, if left to 0, no timeout
85+
-- applies.
8486

8587
function Solve_First
8688
(Self : Relation;
@@ -91,8 +93,10 @@ package Langkit_Support.Adalog.Solver is
9193
-- way to configure the resolution process.
9294
--
9395
-- For the state machine solver, ``Timeout`` determines the maximum number
94-
-- of solving step to run before aborting the solver. If left to 0 or for
95-
-- the symbolic solver, no timeout applies.
96+
-- of solving step to run before aborting the solver. For the symbolic
97+
-- solver, ``Timeout`` determines the maximum of times we evaluate atoms
98+
-- before aborting the solver. In both cases, if left to 0, no timeout
99+
-- applies.
96100

97101
---------------------------
98102
-- Relation constructors --

support/langkit_support-adalog-symbolic_solver.adb

Lines changed: 83 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -186,22 +186,6 @@ package body Langkit_Support.Adalog.Symbolic_Solver is
186186
-- ``Has_Orphan`` is set to whether at least one atom is an "orphan", that
187187
-- is to say it is not part of the resulting sorted collection.
188188

189-
function Evaluate_Atoms
190-
(Sorted_Atoms : Atomic_Relation_Vectors.Elements_Array) return Boolean;
191-
-- Evaluate the given sequence of sorted atoms (see ``Topo_Sort``) and
192-
-- return whether they are all satisfied: if they are, the logic variables
193-
-- are assigned values, so it is possible to invoke the user callback for
194-
-- solutions.
195-
196-
function Has_Contradiction
197-
(Atoms, Unifies : Atomic_Relation_Vector;
198-
Vars : Logic_Var_Array;
199-
Sort_Ctx : in out Sort_Context) return Boolean;
200-
-- Return whether the given sequence of atoms contains a contradiction,
201-
-- i.e. if two or more of its atoms make each other unsatisfied. This
202-
-- function works even for incomplete sequences, for instance when one atom
203-
-- uses a variable that no atom defines.
204-
205189
type Solving_Context is record
206190
Cb : Callback_Type;
207191
-- User callback, to be called when a solution is found. Returns whether
@@ -232,6 +216,10 @@ package body Langkit_Support.Adalog.Symbolic_Solver is
232216
Anys : Any_Relation_Vector;
233217
-- Remaining list of ``Any`` relations to traverse
234218

219+
Timeout : Natural;
220+
-- Number of times left we allow ourselves to evaluate an atom before
221+
-- aborting the solver. If 0, no timeout applies.
222+
235223
Cut_Dead_Branches : Boolean := False;
236224
-- Optimization that will cut branches that necessarily contain falsy
237225
-- solutions.
@@ -263,6 +251,23 @@ package body Langkit_Support.Adalog.Symbolic_Solver is
263251
procedure Destroy (Ctx : in out Solving_Context);
264252
-- Destroy a solving context, and associated data
265253

254+
function Evaluate_Atoms
255+
(Ctx : in out Solving_Context;
256+
Sorted_Atoms : Atomic_Relation_Vectors.Elements_Array) return Boolean;
257+
-- Evaluate the given sequence of sorted atoms (see ``Topo_Sort``) and
258+
-- return whether they are all satisfied: if they are, the logic variables
259+
-- are assigned values, so it is possible to invoke the user callback for
260+
-- solutions.
261+
262+
function Has_Contradiction
263+
(Atoms, Unifies : Atomic_Relation_Vector;
264+
Vars : Logic_Var_Array;
265+
Ctx : in out Solving_Context) return Boolean;
266+
-- Return whether the given sequence of atoms contains a contradiction,
267+
-- i.e. if two or more of its atoms make each other unsatisfied. This
268+
-- function works even for incomplete sequences, for instance when one atom
269+
-- uses a variable that no atom defines.
270+
266271
function Solve_Compound
267272
(Self : Compound_Relation; Ctx : in out Solving_Context) return Boolean;
268273
-- Look for valid solutions in ``Self`` & ``Ctx``. Return whether to
@@ -829,13 +834,60 @@ package body Langkit_Support.Adalog.Symbolic_Solver is
829834
return Sorted_Atoms (1 .. Last_Atom_Index);
830835
end Topo_Sort;
831836

837+
-------------
838+
-- Destroy --
839+
-------------
840+
841+
procedure Destroy (Sort_Ctx : in out Sort_Context) is
842+
begin
843+
Sort_Ctx.N_Preds.Destroy;
844+
Sort_Ctx.Working_Set.Destroy;
845+
for Atoms of Sort_Ctx.Using_Atoms.all loop
846+
Atoms.Destroy;
847+
end loop;
848+
Free (Sort_Ctx.Using_Atoms);
849+
end Destroy;
850+
851+
-------------
852+
-- Destroy --
853+
-------------
854+
855+
procedure Destroy (Ctx : in out Solving_Context) is
856+
begin
857+
Ctx.Unifies.Destroy;
858+
Ctx.Atoms.Destroy;
859+
Ctx.Anys.Destroy;
860+
Destroy (Ctx.Sort_Ctx);
861+
862+
-- Cleanup logic vars for future solver runs using them. Note that no
863+
-- aliasing information is supposed to be left at this stage.
864+
865+
for V of Ctx.Vars.all loop
866+
Reset (V);
867+
Set_Id (V, 0);
868+
end loop;
869+
Free (Ctx.Vars);
870+
end Destroy;
871+
832872
--------------------
833873
-- Evaluate_Atoms --
834874
--------------------
835875

836876
function Evaluate_Atoms
837-
(Sorted_Atoms : Atomic_Relation_Vectors.Elements_Array) return Boolean is
877+
(Ctx : in out Solving_Context;
878+
Sorted_Atoms : Atomic_Relation_Vectors.Elements_Array) return Boolean is
838879
begin
880+
-- If we have a timeout, apply it
881+
882+
if Ctx.Timeout > 0 then
883+
if Sorted_Atoms'Length > Ctx.Timeout then
884+
raise Timeout_Error;
885+
end if;
886+
Ctx.Timeout := Ctx.Timeout - Sorted_Atoms'Length;
887+
end if;
888+
889+
-- Evaluate each individual atom
890+
839891
for Atom of Sorted_Atoms loop
840892
if not Solve_Atomic (Atom) then
841893
if Solv_Trace.Is_Active then
@@ -856,28 +908,28 @@ package body Langkit_Support.Adalog.Symbolic_Solver is
856908
function Has_Contradiction
857909
(Atoms, Unifies : Atomic_Relation_Vector;
858910
Vars : Logic_Var_Array;
859-
Sort_Ctx : in out Sort_Context) return Boolean
911+
Ctx : in out Solving_Context) return Boolean
860912
is
861913
Had_Exception : Boolean := False;
862914
Exc : Exception_Occurrence;
863915

864916
Result : Boolean;
865917
begin
866-
Sort_Ctx.Has_Contradiction_Counter :=
867-
Sort_Ctx.Has_Contradiction_Counter + 1;
918+
Ctx.Sort_Ctx.Has_Contradiction_Counter :=
919+
Ctx.Sort_Ctx.Has_Contradiction_Counter + 1;
868920

869921
if Solv_Trace.Is_Active then
870922
Solv_Trace.Increase_Indent
871923
("Looking for a contradiction (number"
872-
& Sort_Ctx.Has_Contradiction_Counter'Image & ")");
924+
& Ctx.Sort_Ctx.Has_Contradiction_Counter'Image & ")");
873925
Solv_Trace.Trace (Image (Atoms));
874926
end if;
875927

876928
declare
877929
use Atomic_Relation_Vectors;
878930
Dummy : Boolean;
879931
Sorted_Atoms : constant Elements_Array :=
880-
Topo_Sort (Atoms, Unifies, Vars, Sort_Ctx, Dummy);
932+
Topo_Sort (Atoms, Unifies, Vars, Ctx.Sort_Ctx, Dummy);
881933
begin
882934
if Solv_Trace.Is_Active then
883935
Solv_Trace.Trace ("After partial topo sort");
@@ -897,7 +949,7 @@ package body Langkit_Support.Adalog.Symbolic_Solver is
897949
-- the order in which the solver finds solutions.
898950

899951
begin
900-
Result := not Evaluate_Atoms (Sorted_Atoms);
952+
Result := not Evaluate_Atoms (Ctx, Sorted_Atoms);
901953
exception
902954
when E : others =>
903955
Save_Occurrence (Exc, E);
@@ -927,41 +979,6 @@ package body Langkit_Support.Adalog.Symbolic_Solver is
927979
end;
928980
end Has_Contradiction;
929981

930-
-------------
931-
-- Destroy --
932-
-------------
933-
934-
procedure Destroy (Sort_Ctx : in out Sort_Context) is
935-
begin
936-
Sort_Ctx.N_Preds.Destroy;
937-
Sort_Ctx.Working_Set.Destroy;
938-
for Atoms of Sort_Ctx.Using_Atoms.all loop
939-
Atoms.Destroy;
940-
end loop;
941-
Free (Sort_Ctx.Using_Atoms);
942-
end Destroy;
943-
944-
-------------
945-
-- Destroy --
946-
-------------
947-
948-
procedure Destroy (Ctx : in out Solving_Context) is
949-
begin
950-
Ctx.Unifies.Destroy;
951-
Ctx.Atoms.Destroy;
952-
Ctx.Anys.Destroy;
953-
Destroy (Ctx.Sort_Ctx);
954-
955-
-- Cleanup logic vars for future solver runs using them. Note that no
956-
-- aliasing information is supposed to be left at this stage.
957-
958-
for V of Ctx.Vars.all loop
959-
Reset (V);
960-
Set_Id (V, 0);
961-
end loop;
962-
Free (Ctx.Vars);
963-
end Destroy;
964-
965982
--------------
966983
-- Used_Var --
967984
--------------
@@ -1140,7 +1157,7 @@ package body Langkit_Support.Adalog.Symbolic_Solver is
11401157

11411158
-- Once the topological sort has been done, we just have to solve
11421159
-- every relation in order. Abort if one doesn't solve.
1143-
if not Evaluate_Atoms (Sorted_Atoms) then
1160+
if not Evaluate_Atoms (Ctx, Sorted_Atoms) then
11441161
return Cleanup (True);
11451162
end if;
11461163

@@ -1351,7 +1368,7 @@ package body Langkit_Support.Adalog.Symbolic_Solver is
13511368

13521369
Create_Aliases;
13531370
if Has_Contradiction
1354-
(Ctx.Atoms, Ctx.Unifies, Ctx.Vars.all, Ctx.Sort_Ctx)
1371+
(Ctx.Atoms, Ctx.Unifies, Ctx.Vars.all, Ctx)
13551372
then
13561373
if Solv_Trace.Active then
13571374
Solv_Trace.Trace ("Aborting due to exp res optim");
@@ -1426,7 +1443,8 @@ package body Langkit_Support.Adalog.Symbolic_Solver is
14261443
(Self : Relation;
14271444
Solution_Callback : access function
14281445
(Vars : Logic_Var_Array) return Boolean;
1429-
Solve_Options : Solve_Options_Type := Default_Options)
1446+
Solve_Options : Solve_Options_Type := Default_Options;
1447+
Timeout : Natural)
14301448
is
14311449
PRel : Prepared_Relation;
14321450
Rel : Relation renames PRel.Rel;
@@ -1454,6 +1472,7 @@ package body Langkit_Support.Adalog.Symbolic_Solver is
14541472

14551473
PRel := Prepare_Relation (Self);
14561474
Ctx := Create (Solution_Callback'Unrestricted_Access.all, PRel.Vars);
1475+
Ctx.Timeout := Timeout;
14571476
Ctx.Cut_Dead_Branches := Solve_Options.Cut_Dead_Branches;
14581477

14591478
declare
@@ -1506,7 +1525,8 @@ package body Langkit_Support.Adalog.Symbolic_Solver is
15061525

15071526
function Solve_First
15081527
(Self : Relation;
1509-
Solve_Options : Solve_Options_Type := Default_Options) return Boolean
1528+
Solve_Options : Solve_Options_Type := Default_Options;
1529+
Timeout : Natural) return Boolean
15101530
is
15111531
Ret : Boolean := False;
15121532

@@ -1550,7 +1570,7 @@ package body Langkit_Support.Adalog.Symbolic_Solver is
15501570
end Callback;
15511571

15521572
begin
1553-
Solve (Self, Callback'Access, Solve_Options);
1573+
Solve (Self, Callback'Access, Solve_Options, Timeout);
15541574
if Tracked_Vars /= null then
15551575
for TV of Tracked_Vars.all loop
15561576
if TV.Defined then

support/langkit_support-adalog-symbolic_solver.ads

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,27 @@ package Langkit_Support.Adalog.Symbolic_Solver is
5757
(Self : Relation;
5858
Solution_Callback : access function
5959
(Vars : Logic_Var_Array) return Boolean;
60-
Solve_Options : Solve_Options_Type := Default_Options);
60+
Solve_Options : Solve_Options_Type := Default_Options;
61+
Timeout : Natural);
6162
-- Run the solver on the ``Self`` relation. For every solution found, call
6263
-- ``Solution_Callback`` with the variables involved in ``Self``, and
6364
-- continue looking for other solutions iff it returns True. See
6465
-- ``Solve_Options Type`` for the available way to configure the resolution
6566
-- process.
67+
--
68+
-- ``Timeout`` determines the maximum of times we evaluate atoms before
69+
-- aborting the solver. If left to 0, no timeout applies.
6670

6771
function Solve_First
6872
(Self : Relation;
69-
Solve_Options : Solve_Options_Type := Default_Options) return Boolean;
73+
Solve_Options : Solve_Options_Type := Default_Options;
74+
Timeout : Natural) return Boolean;
7075
-- Run the solver on the ``Self`` relation. Return whether there is at
7176
-- least one valid solution. See ``Solve_Options Type`` for the available
7277
-- way to configure the resolution process.
78+
--
79+
-- ``Timeout`` determines the maximum of times we evaluate atoms before
80+
-- aborting the solver. If left to 0, no timeout applies.
7381

7482
function Image (Self : Relation) return String;
7583
-- Return a textual representation of ``Self`` as a multi-line string

0 commit comments

Comments
 (0)