Skip to content

Commit 3b72462

Browse files
authored
Performance memory addbacktrackobj2backtrack (#260)
* use VariableChanges struct with two arrays
1 parent 77c0adf commit 3b72462

31 files changed

+221
-65
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# ConstrainSolver.jl - Changelog
22

3+
## Unreleased
4+
- different data structure for saving variable changes for a speedup of ~10-25% in most instances [PR #260](https://github.com/Wikunia/ConstraintSolver.jl/pull/260)
5+
36
## v0.6.6 (8th of March 2021)
47
- Bugfix: `binary` variables in `TableSet` might have failed because `init_vals` wasn't copied [PR #259](https://github.com/Wikunia/ConstraintSolver.jl/pull/259)
58
- Refactoring constraints

benchmark/benchmarks.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@ const SUITE = BenchmarkGroup()
1010

1111
dir = pkgdir(ConstraintSolver)
1212
include(joinpath(dir, "benchmark/sudoku/benchmark.jl"))
13+
include(joinpath(dir, "benchmark/sudoku/data/25x25_gecode.jl"))
1314

1415
SUITE["sudoku"] = BenchmarkGroup(["alldifferent"])
1516
sudoku_grids = from_file(joinpath(dir, "benchmark/sudoku/data/top95.txt"))
17+
# compiling run to make sure...
18+
solve_sudoku(sudoku_grids[2])
1619
for i in 1:5:95
17-
SUITE["sudoku"]["top95_$i"] = @benchmarkable solve_sudoku($sudoku_grids[$i]) seconds = 2
20+
SUITE["sudoku"]["top95_$i"] = @benchmarkable solve_sudoku($sudoku_grids[$i]) seconds = 1
1821
end
22+
SUITE["sudoku"]["25x25_89"] = @benchmarkable solve_sudoku($sudoku25[89]) seconds = 25
23+
SUITE["sudoku"]["25x25_90"] = @benchmarkable solve_sudoku($sudoku25[90]) seconds = 25
1924

2025
include(joinpath(dir, "benchmark/eternity/benchmark.jl"))
2126

benchmark/eternity/benchmark.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ function get_rotations(puzzle)
2828
end
2929

3030
function solve_eternity(
31-
fname = "eternity_7";
31+
fname = "eternity_6x6";
3232
height = nothing,
3333
width = nothing,
3434
all_solutions = false,
@@ -147,5 +147,6 @@ function solve_eternity(
147147
optimize!(m)
148148

149149
status = JuMP.termination_status(m)
150+
@show status
150151
@assert status == MOI.OPTIMAL
151152
end

benchmark/sudoku/benchmark.jl

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,27 @@ function from_file(filename, sep = '\n')
77
for str_sudoku in str_sudokus
88
str_sudoku = replace(str_sudoku, "." => "0")
99
one_line_grid = parse.(Int, split(str_sudoku, ""))
10-
grid = reshape(one_line_grid, 9, 9)
10+
nvals = length(one_line_grid)
11+
side_len = isqrt(nvals)
12+
grid = reshape(one_line_grid, side_len, side_len)
1113
push!(grids, grid)
1214
end
1315
return grids
1416
end
1517

1618
function solve_sudoku(grid)
1719
m = CS.Optimizer(logging = [])
20+
side_len = size(grid, 1)
21+
block_size = isqrt(side_len)
1822

19-
x = [[MOI.add_constrained_variable(m, MOI.Integer()) for i in 1:9] for j in 1:9]
20-
for r in 1:9, c in 1:9
23+
x = [[MOI.add_constrained_variable(m, MOI.Integer()) for i in 1:side_len] for j in 1:side_len]
24+
for r in 1:side_len, c in 1:side_len
2125
MOI.add_constraint(m, x[r][c][1], MOI.GreaterThan(1.0))
22-
MOI.add_constraint(m, x[r][c][1], MOI.LessThan(9.0))
26+
MOI.add_constraint(m, x[r][c][1], MOI.LessThan(convert(Float64, side_len)))
2327
end
2428

2529
# set variables
26-
for r in 1:9, c in 1:9
30+
for r in 1:side_len, c in 1:side_len
2731
if grid[r, c] != 0
2832
sat = [MOI.ScalarAffineTerm(1.0, x[r][c][1])]
2933
MOI.add_constraint(
@@ -34,31 +38,32 @@ function solve_sudoku(grid)
3438
end
3539
end
3640
# sudoku constraints
37-
for r in 1:9
41+
for r in 1:side_len
3842
MOI.add_constraint(
3943
m,
40-
MOI.VectorOfVariables([x[r][c][1] for c in 1:9]),
41-
CS.AllDifferentSetInternal(9),
44+
MOI.VectorOfVariables([x[r][c][1] for c in 1:side_len]),
45+
CS.AllDifferentSetInternal(side_len),
4246
)
4347
end
44-
for c in 1:9
48+
for c in 1:side_len
4549
MOI.add_constraint(
4650
m,
47-
MOI.VectorOfVariables([x[r][c][1] for r in 1:9]),
48-
CS.AllDifferentSetInternal(9),
51+
MOI.VectorOfVariables([x[r][c][1] for r in 1:side_len]),
52+
CS.AllDifferentSetInternal(side_len),
4953
)
5054
end
51-
variables = [MOI.VariableIndex(0) for _ in 1:9]
52-
for br in 0:2
53-
for bc in 0:2
55+
variables = [MOI.VariableIndex(0) for _ in 1:side_len]
56+
for br in 0:block_size-1
57+
for bc in 0:block_size-1
5458
variables_i = 1
55-
for i in (br * 3 + 1):((br + 1) * 3), j in (bc * 3 + 1):((bc + 1) * 3)
59+
for i in (br * block_size + 1):((br + 1) * block_size), j in (bc * block_size + 1):((bc + 1) * block_size)
5660
variables[variables_i] = x[i][j][1]
5761
variables_i += 1
5862
end
59-
MOI.add_constraint(m, variables, CS.AllDifferentSetInternal(9))
63+
MOI.add_constraint(m, variables, CS.AllDifferentSetInternal(side_len))
6064
end
6165
end
6266

6367
MOI.optimize!(m)
68+
@assert MOI.get(m, MOI.TerminationStatus()) == MOI.OPTIMAL
6469
end

benchmark/sudoku/data/25x25_gecode.jl

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
sudoku25 = Dict(
2+
:89 =>
3+
[
4+
11 23 13 10 19 16 6 2 24 7 5 9 1 20 17 15 8 18 25 3 4 12 21 22 14;
5+
15 16 0 22 0 11 8 0 0 0 25 0 14 0 0 0 12 19 0 0 17 0 0 0 0;
6+
0 0 0 0 0 0 0 0 0 0 0 16 0 4 0 17 0 13 0 24 0 23 19 10 2;
7+
0 0 0 0 0 19 0 14 23 4 0 21 6 22 10 0 11 0 2 0 0 0 0 0 0;
8+
17 14 0 0 2 0 0 13 12 0 0 0 0 0 15 4 20 22 10 0 11 0 9 24 8;
9+
22 0 0 0 0 6 2 0 0 0 4 7 12 1 9 0 0 0 0 0 0 14 5 0 0;
10+
0 18 2 0 8 22 0 19 16 21 0 0 0 10 13 23 0 0 20 0 0 3 0 15 7;
11+
0 0 17 3 0 5 0 0 8 9 0 0 0 0 18 0 19 0 0 0 0 0 23 21 0;
12+
1 11 0 0 9 0 15 10 25 0 6 0 23 0 0 0 0 5 3 7 0 17 0 0 24;
13+
0 0 0 0 0 0 1 0 0 23 0 0 0 24 0 0 0 21 12 0 6 8 0 25 16;
14+
20 24 10 0 15 23 11 17 0 0 0 0 0 7 0 12 0 0 0 0 0 22 0 0 6;
15+
4 5 0 14 12 25 0 18 0 0 23 0 15 0 19 1 0 0 0 22 20 0 7 9 0;
16+
18 0 21 0 0 8 0 24 0 0 9 0 25 0 0 0 10 0 0 0 2 0 1 19 0;
17+
0 0 6 2 1 0 13 0 22 0 0 0 0 0 11 8 21 16 0 0 25 0 0 12 17;
18+
0 17 25 0 23 7 14 0 21 1 0 0 0 0 3 0 0 11 0 0 24 0 16 4 5;
19+
0 0 0 0 11 18 24 0 0 0 0 5 0 12 0 25 0 0 0 15 23 4 8 14 0;
20+
0 0 0 15 21 0 0 0 0 0 2 0 13 17 0 0 1 7 0 0 5 9 24 0 0;
21+
0 0 18 0 22 15 0 0 2 16 0 23 0 0 0 10 6 24 0 17 12 0 25 11 0;
22+
7 2 0 1 0 0 21 0 0 0 18 22 0 9 6 14 0 4 5 16 0 0 0 0 0;
23+
0 0 9 0 0 0 7 22 0 0 10 0 24 0 0 0 18 0 0 0 21 0 0 0 0;
24+
0 12 0 19 10 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 14 0 4 8 0;
25+
24 0 11 18 0 0 0 0 0 0 0 25 17 21 0 6 0 0 1 0 0 0 0 5 12;
26+
16 6 22 0 0 0 23 4 15 18 8 0 0 0 20 0 0 17 0 14 0 0 0 0 0;
27+
0 21 0 0 4 0 9 1 7 0 0 0 0 11 14 0 16 8 15 0 22 0 18 0 0;
28+
8 15 0 0 0 0 0 0 5 0 24 3 0 0 4 0 0 0 9 0 0 0 0 0 20;
29+
],
30+
31+
:90 =>
32+
[
33+
0 23 13 0 19 16 6 0 24 7 5 9 1 0 0 15 8 18 25 0 4 0 21 22 0;
34+
15 16 0 22 0 11 8 0 0 0 25 0 14 0 0 0 12 19 0 0 17 0 0 0 0;
35+
0 0 0 0 0 0 0 0 0 0 0 16 0 4 0 17 0 13 0 24 0 23 19 10 2;
36+
0 0 0 0 0 19 0 14 23 4 0 21 6 22 10 0 11 0 2 0 0 0 0 0 0;
37+
17 14 0 0 2 0 0 13 12 0 0 0 0 0 15 4 20 22 10 0 11 0 9 24 8;
38+
22 0 0 0 0 6 2 0 0 0 4 7 12 1 9 0 0 0 0 0 0 14 5 0 0;
39+
0 18 2 0 8 22 0 19 16 21 0 0 0 10 13 23 0 0 20 0 0 3 0 15 7;
40+
0 0 17 3 0 5 0 0 8 9 0 0 0 0 18 0 19 0 0 0 0 0 23 21 0;
41+
1 11 0 0 9 0 15 10 25 0 6 0 23 0 0 0 0 5 3 7 0 17 0 0 24;
42+
0 0 0 0 0 0 1 0 0 23 0 0 0 24 0 0 0 21 12 0 6 8 0 25 16;
43+
20 24 10 0 15 23 11 17 0 0 0 0 0 7 0 12 0 0 0 0 0 22 0 0 6;
44+
4 5 0 14 12 25 0 18 0 0 23 0 15 0 19 1 0 0 0 22 20 0 7 9 0;
45+
18 0 21 0 0 8 0 24 0 0 9 0 25 0 0 0 10 0 0 0 2 0 1 19 0;
46+
0 0 6 2 1 0 13 0 22 0 0 0 0 0 11 8 21 16 0 0 25 0 0 12 17;
47+
0 17 25 0 23 7 14 0 21 1 0 0 0 0 3 0 0 11 0 0 24 0 16 4 5;
48+
0 0 0 0 11 18 24 0 0 0 0 5 0 12 0 25 0 0 0 15 23 4 8 14 0;
49+
0 0 0 15 21 0 0 0 0 0 2 0 13 17 0 0 1 7 0 0 5 9 24 0 0;
50+
0 0 18 0 22 15 0 0 2 16 0 23 0 0 0 10 6 24 0 17 12 0 25 11 0;
51+
7 2 0 1 0 0 21 0 0 0 18 22 0 9 6 14 0 4 5 16 0 0 0 0 0;
52+
0 0 9 0 0 0 7 22 0 0 10 0 24 0 0 0 18 0 0 0 21 0 0 0 0;
53+
0 12 0 19 10 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 14 0 4 8 0;
54+
24 0 11 18 0 0 0 0 0 0 0 25 17 21 0 6 0 0 1 0 0 0 0 5 12;
55+
16 6 22 0 0 0 23 4 15 18 8 0 0 0 20 0 0 17 0 14 0 0 0 0 0;
56+
0 21 0 0 4 0 9 1 7 0 0 0 0 11 14 0 16 8 15 0 22 0 18 0 0;
57+
8 15 0 0 0 0 0 0 5 0 24 3 0 0 4 0 0 0 9 0 0 0 0 0 20;
58+
]
59+
)

benchmark/sudoku/data/Readme.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# top95, easy50 & hardest
2+
3+
https://github.com/dimitri/sudoku
4+
5+
# 25x25 gecode
6+
7+
http://www.gecode.org/gecode-doc-latest/sudoku08cpp-source.html

src/ConstraintSolver.jl

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,6 @@ function addBacktrackObj2Backtrack_vec!(backtrack_vec, backtrack_obj, com::CS.Co
295295
@assert length(backtrack_vec) == backtrack_obj.idx
296296
add2priorityqueue(com, backtrack_obj)
297297

298-
for v in com.search_space
299-
push!(v.changes, Vector{Tuple{Symbol,Int,Int,Int}}())
300-
end
301298
create_log_node(com)
302299
end
303300

@@ -577,7 +574,6 @@ function backtrack!(
577574
vidx = backtrack_obj.vidx
578575

579576
com.c_backtrack_idx = backtrack_obj.idx
580-
581577
checkout_new_node!(com, last_backtrack_id, backtrack_obj.idx)
582578

583579
# if backtracking was started

src/MOI_wrapper/variables.jl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,6 @@ end
5757
function MOI.add_variable(model::Optimizer)
5858
vidx = length(model.variable_info) + 1
5959
push!(model.variable_info, Variable(vidx))
60-
changes = changes = Vector{Vector{Tuple{Symbol,Int,Int,Int}}}()
61-
push!(changes, Vector{Tuple{Symbol,Int,Int,Int}}())
62-
model.variable_info[vidx].changes = changes
6360
push!(model.inner.subscription, Int[])
6461
push!(model.inner.bt_infeasible, 0)
6562
push!(model.inner.var_in_obj, false)

src/Variable.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ function rm!(
8989
v.max = maximum(vals)
9090
end
9191
end
92-
changes && push!(v.changes[com.c_backtrack_idx], (:rm, x, 0, 1))
92+
changes && push_to_changes!(v, (:rm, x, 0, 1))
9393
end
9494
return true
9595
end
@@ -103,7 +103,7 @@ function fix!(com::CS.CoM, v::CS.Variable, x::Int; changes = true, check_feasibi
103103
vidx = v.indices[x + v.offset]
104104
pr_below = vidx - v.first_ptr
105105
pr_above = v.last_ptr - vidx
106-
changes && push!(v.changes[com.c_backtrack_idx], (:fix, x, v.last_ptr, 0))
106+
changes && push_to_changes!(v, (:fix, x, v.last_ptr, 0))
107107
v.last_ptr = vidx
108108
v.first_ptr = vidx
109109
v.min = x
@@ -149,7 +149,7 @@ function remove_below!(
149149
if nremoved > 0 && feasible(var)
150150
var.min = minimum(values(var))
151151
changes &&
152-
push!(var.changes[com.c_backtrack_idx], (:remove_below, val, 0, nremoved))
152+
push_to_changes!(var, (:remove_below, val, 0, nremoved))
153153
end
154154
return true
155155
end
@@ -188,7 +188,7 @@ function remove_above!(
188188
if nremoved > 0 && feasible(var)
189189
var.max = maximum(values(var))
190190
changes &&
191-
push!(var.changes[com.c_backtrack_idx], (:remove_above, val, 0, nremoved))
191+
push_to_changes!(var, (:remove_above, val, 0, nremoved))
192192
end
193193
return true
194194
end

src/branching.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ Return lb, leq, geq, ub => the bounds for the lower part and the bounds for the
3434
"""
3535
function get_split_pvals(com, ::Val{:InHalf}, var::Variable)
3636
pvals = values(var)
37-
@assert length(pvals) >= 2
3837
mean_val = mean(pvals)
3938
leq = typemin(Int)
4039
geq = typemax(Int)
@@ -215,7 +214,7 @@ function probe_until(com::CS.CoM)
215214
backtrack_obj.parent_idx = 1
216215
backtrack_obj.depth = 1
217216
com.c_step_nr += 1
218-
backtrack_obj.step_nr = com.c_step_nr
217+
set_current_step_nr!(backtrack_obj, com)
219218

220219
addBacktrackObj2Backtrack_vec!(com.backtrack_vec, backtrack_obj, com)
221220
update_log_node!(com, 2)
@@ -312,6 +311,7 @@ end
312311

313312
function update_activity!(com)
314313
c_backtrack_idx = com.c_backtrack_idx
314+
c_step_nr = com.c_step_nr
315315
backtrack_obj = com.backtrack_vec[c_backtrack_idx]
316316
branch_vidx = backtrack_obj.vidx
317317
γ = com.options.activity.decay
@@ -320,19 +320,20 @@ function update_activity!(com)
320320
if nvalues(variable) > 1
321321
variable.activity *= γ
322322
end
323-
if length(variable.changes[c_backtrack_idx]) > 0
323+
if num_changes(variable, c_step_nr) > 0
324324
variable.activity += 1
325325
end
326326
end
327327
end
328328

329329
function update_probe_activity!(activities, com)
330330
c_backtrack_idx = com.c_backtrack_idx
331+
c_step_nr = com.c_step_nr
331332
backtrack_obj = com.backtrack_vec[c_backtrack_idx]
332333
branch_vidx = backtrack_obj.vidx
333334
for variable in com.search_space
334335
variable.idx == branch_vidx && continue
335-
if length(variable.changes[c_backtrack_idx]) > 0
336+
if num_changes(variable, c_step_nr) > 0
336337
activities[variable.idx] += 1
337338
end
338339
end

0 commit comments

Comments
 (0)