Skip to content

Commit f519118

Browse files
author
Issa Hanou
committed
Update ASP solver and fully test it
1 parent 6ba371c commit f519118

File tree

4 files changed

+141
-49
lines changed

4 files changed

+141
-49
lines changed

src/HerbConstraints.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,6 @@ include("solver/uniform_solver/uniform_solver.jl")
5959
include("solver/uniform_solver/uniform_treemanipulations.jl")
6060
include("solver/domainutils.jl")
6161

62-
include("solver/uniform_solver/asp/asp_tree_transformations.jl")
63-
include("solver/uniform_solver/asp/asp_constraint_transformations.jl")
64-
include("solver/uniform_solver/asp/asp_uniform_tree_solver.jl")
65-
6662
include("patternmatch.jl")
6763
include("lessthanorequal.jl")
6864
include("makeequal.jl")
@@ -81,6 +77,10 @@ include("grammarconstraints/contains_subtree.jl")
8177
include("grammarconstraints/forbidden_sequence.jl")
8278
include("grammarconstraints/unique.jl")
8379

80+
include("solver/uniform_solver/asp/asp_tree_transformations.jl")
81+
include("solver/uniform_solver/asp/asp_constraint_transformations.jl")
82+
include("solver/uniform_solver/asp/asp_uniform_tree_solver.jl")
83+
8484
export
8585
AbstractGrammarConstraint,
8686
AbstractLocalConstraint,

src/solver/uniform_solver/asp/asp_constraint_transformations.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ is_smaller(X,Y) :- node(X,XV), node(Y,YV), XV = YV, S = #sum {Z: child(X,Z,XC),
9595
:- node(X1,5),child(X1,1,X),child(X1,2,Y),child(X1,3,Z) not is_smaller(Y,Z).
9696
"""
9797
function to_ASP(grammar::AbstractGrammar, constraint::Ordered, constraint_index::Int64)
98+
# TODO use order property of constraint
9899
output = "is_smaller(X,Y) :- node(X,XV),node(Y,YV),XV < YV.\n"
99100
output *= "is_smaller(X,Y) :- node(X,XV),node(Y,YV),XV = YV,S = #sum { Z : child(X,Z,XC),child(Y,Z,YC),is_smaller(XC,YC) }, M = #max { Z : child(X,Z,XC) }, S = M.\n"
100101

src/solver/uniform_solver/asp/asp_uniform_tree_solver.jl

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,64 @@
1-
struct ASPSolver <: Solver
1+
using Clingo_jll
2+
3+
mutable struct ASPSolver <: Solver
24
grammar::AbstractGrammar
3-
tree::Union{RuleNode,StateHole}
5+
tree::Union{RuleNode,UniformHole,StateHole}
46
solutions::Vector{Dict{Int64,Int64}} #vector of dictionaries with key=node and value=matching rule index
5-
tmp_file_name::String
7+
isfeasible::Bool
8+
statistics::Union{TimerOutput, Nothing}
9+
end
10+
11+
"""
12+
ASPSolver(grammar::AbstractGrammar, fixed_shaped_tree::AbstractRuleNode)
13+
"""
14+
function ASPSolver(grammar::AbstractGrammar, fixed_shaped_tree::AbstractRuleNode; with_statistics=false)
15+
@assert !contains_nonuniform_hole(fixed_shaped_tree) "$(fixed_shaped_tree) contains non-uniform holes"
16+
statistics = @match with_statistics begin
17+
::TimerOutput => with_statistics
18+
::Bool => with_statistics ? TimerOutput("ASP Solver") : nothing
19+
::Nothing => nothing
20+
end
21+
solver = ASPSolver(grammar, fixed_shaped_tree, Vector{Dict{Int32,Int32}}(), false, statistics)
22+
solve(solver)
23+
return solver
624
end
725

826
get_name(::ASPSolver) = "ASPSolver"
927

10-
get_tree(solver::ASPSolver) = solver.tree
28+
"""
29+
get_grammar(solver::ASPSolver)
30+
31+
Get the grammar.
32+
"""
33+
function get_grammar(solver::ASPSolver)::AbstractGrammar
34+
return solver.grammar
35+
end
1136

12-
get_grammar(solver::ASPSolver) = solver.grammar
37+
"""
38+
get_tree(solver::ASPSolver)
39+
40+
Get the root of the tree. This remains the same instance throughout the entire search.
41+
"""
42+
function get_tree(solver::ASPSolver)::AbstractRuleNode
43+
return solver.tree
44+
end
1345

14-
get_filename(solver::ASPSolver) = solver.tmp_file_name
46+
"""
47+
isfeasible(solver::ASPSolver)
1548
16-
get_resultsfile(solver::ASPSolver) = replace(get_filename(solver), ".lp" => "_res.lp")
49+
Returns true if no inconsistency has been detected.
50+
"""
51+
function isfeasible(solver::ASPSolver)
52+
return solver.isfeasible
53+
end
1754

18-
ASPSolver(grammar, tree) = ASPSolver(grammar, tree, Vector{Dict{Int32,Int32}}(), "__tmp_asp_file.lp")
55+
"""
56+
solve(solver::ASPSolver)
1957
20-
function solve(solver::ASPSolver, write_to_file::Bool=false)
58+
Generate all solutions for the given tree using ASP solver Clingo.
59+
"""
60+
function solve(solver::ASPSolver)
61+
@timeit_debug solver.statistics "generate ASP tree" begin end
2162
string_tree, _ = tree_to_ASP(get_tree(solver), get_grammar(solver), 1)
2263
constraints = to_ASP(get_grammar(solver))
2364
asp_input = """
@@ -32,37 +73,31 @@ $constraints
3273
"""
3374
buffer = IOBuffer(asp_input)
3475
asp_output = IOBuffer()
76+
@timeit_debug solver.statistics "run Clingo" begin end
3577
run(pipeline(ignorestatus(`$(Clingo_jll.clingo()) --models 0`), stdin=buffer, stdout=asp_output))
3678
extract_solutions(solver, split(String(take!(asp_output)), "\n"))
37-
if write_to_file
38-
# Write the asp program to a file
39-
open(get_filename(solver), "w") do file
40-
write(file, String(take!(buffer)))
41-
end
42-
# Write the asp output to a file
43-
open(get_resultsfile(solver), "w") do file
44-
write(file, String(take!(asp_output)))
45-
end
46-
end
47-
end
48-
49-
function extract_solutions_from_file(solver::ASPSolver)
50-
open(get_resultsfile(solver), "r") do file
51-
extract_solutions(solver, eachline(file))
52-
end
5379
end
5480

81+
"""
82+
extract_solutions(solver::ASPSolver, output_lines)
83+
Extract solutions from the output of Clingo and store them in the `solutions` field of the solver.
84+
"""
5585
function extract_solutions(solver::ASPSolver, output_lines)
56-
current_solution = Dict{Int64,Int64}()
86+
@timeit_debug solver.statistics "extract solutions" begin end
5787
for line in output_lines
5888
if startswith(line, "node")
89+
current_solution = Dict{Int64,Int64}()
5990
node_assignments = split(line, " ")
6091
for node in node_assignments
6192
m = match(r"node\((\d+),(\d+)\)", node)
6293
current_solution[parse(Int, m.captures[1])] = parse(Int, m.captures[2])
6394
end
6495
push!(solver.solutions, current_solution)
6596
elseif startswith(line, "SATISFIABLE")
97+
solver.isfeasible = true
98+
break
99+
elseif startswith(line, "UNSATISFIABLE")
100+
solver.isfeasible = false
66101
break
67102
end
68103
end

test/test_asp_solver.jl

Lines changed: 75 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -242,29 +242,52 @@ subtree(c1) :- node(X1,4),child(X1,1,X2),node(X2,D2),allowed(c1x2,D2),child(X1,2
242242
end
243243

244244
@testset "ASPSolver" begin
245-
@testset "asp_solver" begin
245+
@testset "asp_solver_uniform_holes" begin
246+
g = @csgrammar begin
247+
S = 1 | x
248+
S = S + S
249+
S = S * S
250+
end
251+
addconstraint!(g, Unique(1))
252+
addconstraint!(g, Unique(2))
253+
254+
tree = UniformHole(BitVector((0, 0, 1, 1)), [
255+
UniformHole(BitVector((1, 1, 0, 0)), []),
256+
UniformHole(BitVector((1, 1, 0, 0)), [])
257+
])
258+
259+
solver = ASPSolver(g, tree)
260+
@test length(solver.solutions) == 4
261+
262+
@test Dict{Int64,Int64}(1 => 3, 2 => 1, 3 => 2) in solver.solutions
263+
@test Dict{Int64,Int64}(1 => 3, 2 => 2, 3 => 1) in solver.solutions
264+
@test Dict{Int64,Int64}(1 => 4, 2 => 1, 3 => 2) in solver.solutions
265+
@test Dict{Int64,Int64}(1 => 4, 2 => 2, 3 => 1) in solver.solutions
266+
end
267+
268+
@testset "asp_solver_filled_tree" begin
246269
g = @csgrammar begin
247270
S = 1 | x
248271
S = S + S
249272
S = S * S
250273
end
251274

252275
tree = RuleNode(3, [
253-
RuleNode(1),
254276
RuleNode(4, [
255277
RuleNode(1),
256278
RuleNode(2)
257-
])
279+
]),
280+
RuleNode(3, [
281+
RuleNode(1),
282+
RuleNode(2)
283+
]),
258284
])
259285

260286
solver = ASPSolver(g, tree)
261-
solve(solver)
262287
@test length(solver.solutions) == 1
263-
expected_solution = Dict{Int64,Int64}(1 => 3, 2 => 1, 3 => 4, 4 => 1, 5 => 2)
264-
@test solver.solutions[1] == expected_solution
265288
end
266-
267-
@testset "asp_solver_write_file" begin
289+
290+
@testset "asp_solver_filled_tree_constraints" begin
268291
g = @csgrammar begin
269292
S = 1 | x
270293
S = S + S
@@ -273,22 +296,55 @@ subtree(c1) :- node(X1,4),child(X1,1,X2),node(X2,D2),allowed(c1x2,D2),child(X1,2
273296
addconstraint!(g, Unique(1))
274297
addconstraint!(g, Unique(2))
275298

276-
tree = RuleNode(4, [
277-
UniformHole(BitVector((1, 1, 0, 0)), []),
278-
UniformHole(BitVector((1, 1, 0, 0)), [])
299+
tree = RuleNode(3, [
300+
RuleNode(4, [
301+
RuleNode(1),
302+
RuleNode(2)
303+
]),
304+
RuleNode(3, [
305+
RuleNode(1),
306+
RuleNode(2)
307+
]),
279308
])
280309

281310
solver = ASPSolver(g, tree)
282-
solve(solver, true)
311+
@test length(solver.solutions) == 0
312+
@test isfeasible(solver) == false
313+
end
314+
315+
@testset "asp_solver_non_uniform" begin
316+
g = @csgrammar begin
317+
S = 1 | x
318+
S = S + S
319+
S = S * S
320+
end
321+
322+
tree = Hole(BitVector([1, 1, 1, 1]))
323+
324+
try
325+
solver = ASPSolver(g, tree)
326+
catch AssertionError
327+
@test true
328+
end
329+
end
330+
331+
@testset "asp_solver_properties" begin
332+
g = @csgrammar begin
333+
S = 1 | x
334+
S = S + S
335+
S = S * S
336+
end
283337

284-
extract_solutions_from_file(solver)
285-
@test length(solver.solutions) == 2
286-
287-
expected_solution_1 = Dict{Int64,Int64}(1 => 4, 2 => 1, 3 => 2)
288-
expected_solution_2 = Dict{Int64,Int64}(1 => 4, 2 => 2, 3 => 1)
338+
tree = UniformHole(BitVector((0, 0, 1, 1)), [
339+
UniformHole(BitVector((1, 1, 0, 0)), []),
340+
UniformHole(BitVector((1, 1, 0, 0)), [])
341+
])
289342

290-
@test solver.solutions[1] == expected_solution_1 || solver.solutions[1] == expected_solution_2
291-
@test solver.solutions[2] == expected_solution_1 || solver.solutions[2] == expected_solution_2
343+
solver = ASPSolver(g, tree)
344+
@test get_grammar(solver) === g
345+
@test get_tree(solver) === tree
346+
@test isfeasible(solver) === true
347+
@test get_name(solver) == "ASPSolver"
292348
end
293349
end
294350
end

0 commit comments

Comments
 (0)