Skip to content

Commit 24a0b66

Browse files
authored
Speed up set difference of interval boxes (#456)
* sped up set difference of interval boxes * updated version with less allocations * removed old functions * implemented first suggestion (use static arrays and setindex) * added one test with infinite interval boxes * bumped patch version
1 parent e0333ae commit 24a0b66

File tree

3 files changed

+46
-99
lines changed

3 files changed

+46
-99
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "IntervalArithmetic"
22
uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
33
repo = "https://github.com/JuliaIntervals/IntervalArithmetic.jl.git"
4-
version = "0.18.0"
4+
version = "0.18.1"
55

66
[deps]
77
CRlibm = "96374032-68de-5a5b-8d9e-752f78720389"

src/multidim/setdiff.jl

Lines changed: 25 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,23 @@
11
"""
2-
Returns a list of pairs (interval, label)
3-
label is 1 if the interval is *excluded* from the setdiff
4-
label is 0 if the interval is included in the setdiff
5-
label is -1 if the intersection of the two intervals was empty
2+
_setdiff(x::Interval{T}, y::Interval{T})
3+
4+
Computes the set difference x\\y and always returns a tuple of two intervals.
5+
If the set difference is only one interval or is empty, then the returned tuple contains 1
6+
or 2 empty intervals.
67
"""
7-
function labelled_setdiff(x::Interval{T}, y::Interval{T}) where T
8+
function _setdiff(x::Interval{T}, y::Interval{T}) where T
89
intersection = x y
910

10-
isempty(intersection) && return [(x, -1)]
11-
intersection == x && return [(x, 1)]
11+
isempty(intersection) && return (x, emptyinterval(T))
12+
intersection == x && return (emptyinterval(T), emptyinterval(T)) # x is subset of y; setdiff is empty
1213

13-
x.lo == intersection.lo && return [(intersection, 1), (Interval(intersection.hi, x.hi), 0)]
14-
x.hi == intersection.hi && return [(intersection, 1), (Interval(x.lo, intersection.lo), 0)]
14+
x.lo == intersection.lo && return (Interval(intersection.hi, x.hi), emptyinterval(T))
15+
x.hi == intersection.hi && return (Interval(x.lo, intersection.lo), emptyinterval(T))
1516

16-
return [(y, 1),
17-
(Interval(x.lo, y.lo), 0),
18-
(Interval(y.hi, x.hi), 0)]
17+
return (Interval(x.lo, y.lo), Interval(y.hi, x.hi))
1918

2019
end
2120

22-
# function setdiff{N,T}(A::IntervalBox{N,T}, B::IntervalBox{N,T})
23-
# X = [labelled_setdiff(a,b) for (a, b) in zip(A, B)]
24-
# lengths = map(length, X)
25-
# index = ones(N)
26-
#
27-
# # index[j] represents which set we are looking at in direction j
28-
#``
29-
#
30-
# while index[1] <= N
31-
# current_direction = 1
32-
# current_piece = [ X[1][index[1]] ]
33-
#
34-
# end
35-
# end
3621

3722
"""
3823
setdiff(A::IntervalBox{N,T}, B::IntervalBox{N,T})
@@ -44,65 +29,21 @@ Algorithm: Start from the total overlap (in all directions);
4429
expand each direction in turn.
4530
"""
4631
function setdiff(A::IntervalBox{N,T}, B::IntervalBox{N,T}) where {N,T}
47-
X = [labelled_setdiff(a, b) for (a, b) in zip(A.v, B.v)]
48-
# ordered such that the first in each is the excluded interval
49-
50-
first = [ i[1] for i in X ]
51-
labels = [i[2] for i in first]
52-
53-
any(labels .== -1) && return [A] # no overlap
54-
55-
# @assert all(labels .== 1)
56-
57-
excluded = [i[1] for i in first]
5832

59-
result_list = IntervalBox{N,T}[]
60-
61-
for dimension in N:-1:1
62-
for which in X[dimension][2:end]
63-
excluded[dimension] = which[1]
64-
push!(result_list,
65-
IntervalBox(excluded[1:dimension]..., A[dimension+1:N]...))
33+
intersection = A B
34+
isempty(intersection) && return [A]
35+
36+
result_list = fill(IntervalBox(emptyinterval(T), N), 2*N)
37+
offset = 0
38+
x = A.v
39+
@inbounds for i = 1:N
40+
tmp = _setdiff(A[i], B[i])
41+
@inbounds for j = 1:2
42+
x = setindex(x, tmp[j], i)
43+
result_list[offset+j] = IntervalBox{N, T}(x)
6644
end
45+
offset += 2
46+
x = setindex(x, intersection[i], i)
6747
end
68-
69-
result_list
70-
48+
filter!(!isempty, result_list)
7149
end
72-
73-
74-
# """
75-
# setdiff(A::IntervalBox{2,T}, B::IntervalBox{2,T})
76-
#
77-
# Returns a vector of `IntervalBox`es that are in the set difference `A \ B`,
78-
# i.e. the set of `x` that are in `A` but not in `B`.
79-
# """
80-
# function setdiff{T}(A::IntervalBox{2,T}, B::IntervalBox{2,T})
81-
# X = labelled_setdiff(A[1], B[1])
82-
# Y = labelled_setdiff(A[2], B[2])
83-
#
84-
# results_list = typeof(A)[]
85-
#
86-
# for (x, label) in X
87-
# label == -1 && return [A] # intersection in one direction empty, so total intersection empty
88-
#
89-
# if label == 0
90-
# push!(results_list, IntervalBox(x, A[2]))
91-
# continue
92-
# end
93-
#
94-
# # label is 1 here, so there is some intersection in the x direction
95-
# for (y, label) in Y
96-
# label == -1 && return [A]
97-
#
98-
# if label == 0
99-
# push!(results_list, IntervalBox(x, y))
100-
# continue
101-
# end
102-
#
103-
# # label == 1: exclude this box since all labels are 1
104-
# end
105-
# end
106-
#
107-
# return results_list
108-
# end

test/multidim_tests/multidim.jl

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -108,34 +108,34 @@ end
108108
@testset "setdiff for IntervalBox" begin
109109
X = IntervalBox(2..4, 3..5)
110110
Y = IntervalBox(3..5, 4..6)
111-
@test setdiff(X, Y) == [ IntervalBox(3..4, 3..4),
112-
IntervalBox(2..3, 3..5) ]
111+
@test Set(setdiff(X, Y)) == Set([ IntervalBox(3..4, 3..4),
112+
IntervalBox(2..3, 3..5) ])
113113

114-
@test setdiff(X.v, Y) == [ IntervalBox(3..4, 3..4),
115-
IntervalBox(2..3, 3..5) ]
114+
@test Set(setdiff(X.v, Y)) == Set([ IntervalBox(3..4, 3..4),
115+
IntervalBox(2..3, 3..5) ])
116116

117-
@test setdiff(X, Y.v) == [ IntervalBox(3..4, 3..4),
118-
IntervalBox(2..3, 3..5) ]
117+
@test Set(setdiff(X, Y.v)) == Set([ IntervalBox(3..4, 3..4),
118+
IntervalBox(2..3, 3..5) ])
119119

120120
X = IntervalBox(2..5, 3..6)
121121
Y = IntervalBox(-10..10, 4..5)
122-
@test setdiff(X, Y) == [ IntervalBox(2..5, 3..4),
123-
IntervalBox(2..5, 5..6) ]
122+
@test Set(setdiff(X, Y)) == Set([ IntervalBox(2..5, 3..4),
123+
IntervalBox(2..5, 5..6) ])
124124

125125

126126
X = IntervalBox(2..5, 3..6)
127127
Y = IntervalBox(4..6, 4..5)
128-
@test setdiff(X, Y) == [ IntervalBox(4..5, 3..4),
128+
@test Set(setdiff(X, Y)) == Set([ IntervalBox(4..5, 3..4),
129129
IntervalBox(4..5, 5..6),
130-
IntervalBox(2..4, 3..6) ]
130+
IntervalBox(2..4, 3..6) ])
131131

132132

133133
X = IntervalBox(2..5, 3..6)
134134
Y = IntervalBox(3..4, 4..5)
135-
@test setdiff(X, Y) == [ IntervalBox(3..4, 3..4),
135+
@test Set(setdiff(X, Y)) == Set([ IntervalBox(3..4, 3..4),
136136
IntervalBox(3..4, 5..6),
137137
IntervalBox(2..3, 3..6),
138-
IntervalBox(4..5, 3..6) ]
138+
IntervalBox(4..5, 3..6) ])
139139

140140

141141
X = IntervalBox(2..5, 3..6)
@@ -150,14 +150,20 @@ end
150150

151151
X = IntervalBox(1..4, 3..6, 7..10)
152152
Y = IntervalBox(2..3, 4..5, 8..9)
153-
@test setdiff(X, Y) == [ IntervalBox(2..3, 4..5, 7..8),
153+
@test Set(setdiff(X, Y)) == Set([ IntervalBox(2..3, 4..5, 7..8),
154154
IntervalBox(2..3, 4..5, 9..10),
155155
IntervalBox(2..3, 3..4, 7..10),
156156
IntervalBox(2..3, 5..6, 7..10),
157157
IntervalBox(1..2, 3..6, 7..10),
158-
IntervalBox(3..4, 3..6, 7..10) ]
158+
IntervalBox(3..4, 3..6, 7..10) ])
159159

160160

161+
X = IntervalBox(-Inf..Inf, 1..2)
162+
Y = IntervalBox(1..2, -1..1.5)
163+
164+
@test Set(setdiff(X, Y)) == Set([IntervalBox(-Inf..1, 1..2),
165+
IntervalBox(2..Inf, 1..2),
166+
IntervalBox(1..2, 1.5..2)])
161167
end
162168

163169
@testset "mid, diam, × for IntervalBox" begin

0 commit comments

Comments
 (0)