Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-interval set operators #179

Merged
merged 96 commits into from
May 31, 2022
Merged
Show file tree
Hide file tree
Changes from 93 commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
f1f3b88
wip: initial implementation
haberdashPI Oct 21, 2021
1d0336c
wip: handling edge edge cases
haberdashPI Oct 22, 2021
9665e47
initially working local tests
haberdashPI Oct 22, 2021
a1611d1
fix: random dep
haberdashPI Oct 22, 2021
c153f82
edgedir for type invariance
haberdashPI Oct 22, 2021
48c9ff0
improved comments
haberdashPI Oct 22, 2021
e0c6b40
fix `isdisjoint` definition
haberdashPI Oct 22, 2021
60610d3
fix: typo
haberdashPI Oct 22, 2021
4cbfab7
wip: improve union return type
haberdashPI Oct 22, 2021
73a9e93
wip: some test cleanup
haberdashPI Oct 22, 2021
01fd7eb
improve unbunch
haberdashPI Oct 22, 2021
8d0431e
working local tests
haberdashPI Oct 22, 2021
ea27f97
chore: cleanup (comments/debug removal)
haberdashPI Oct 22, 2021
e2f0120
chore: improve docs
haberdashPI Oct 22, 2021
1a64cac
cleanup: remove `Infiltrator`
haberdashPI Oct 22, 2021
8415143
chore: cleanup comments
haberdashPI Oct 22, 2021
0a7ee07
fix: misplaced eltype
haberdashPI Oct 23, 2021
8280942
chore: improved some comments
haberdashPI Nov 1, 2021
6c3c3f7
wip: unworking revisiong to handle Endpoints
haberdashPI Nov 3, 2021
2d6004b
wip: initial refactor using Endpoints complete
haberdashPI Nov 4, 2021
1ac7e84
set tests working
haberdashPI Nov 9, 2021
a37d7fc
wip fix `isequal` and `==`
haberdashPI Nov 9, 2021
400d9e2
fix: endpoint comparisons
haberdashPI Nov 10, 2021
378d159
fix: endpoint comparisons
haberdashPI Nov 10, 2021
281ec3f
wip: endpoint comparisons
haberdashPI Nov 10, 2021
e33a937
fix: endpoint comparisons
haberdashPI Nov 10, 2021
42b9490
fix missing method
haberdashPI Nov 10, 2021
f0563d9
cleanup: some cruft
haberdashPI Dec 3, 2021
5cfc75f
wip: leveraging interval tests
haberdashPI Dec 3, 2021
2f84342
update interval tests
haberdashPI Dec 14, 2021
2de5e60
revised docs
haberdashPI Dec 14, 2021
8dc71ce
updates tests, fixed edge case
haberdashPI Dec 14, 2021
f64b9b5
fix set test
haberdashPI Dec 14, 2021
0ced04a
removing `Infiltrator` debug package
haberdashPI Dec 15, 2021
406bf61
rename `intersectmap` -> `find_intersections`
haberdashPI Dec 22, 2021
1724b33
revert endpoint changes
haberdashPI Feb 3, 2022
9e95a11
wip
haberdashPI Feb 3, 2022
07ce9cc
wip
haberdashPI Feb 7, 2022
753d501
wip
haberdashPI Feb 7, 2022
f41b291
wip
haberdashPI Feb 9, 2022
4f614ca
bug fixes
haberdashPI Feb 10, 2022
81e0d53
removed temporary test project files
haberdashPI Feb 14, 2022
1ec76b3
add missing test dep
haberdashPI Feb 14, 2022
6265d0f
fix typo in test function
haberdashPI Feb 14, 2022
7af5d80
some small cleanup
haberdashPI Feb 14, 2022
372996d
remove `SimpleEndpoint`
haberdashPI Feb 15, 2022
1d671da
removed temporary test project files
haberdashPI Feb 15, 2022
ee709c5
Cleanup some comments
haberdashPI Feb 15, 2022
c27b776
removed temporary test project files
haberdashPI Feb 15, 2022
7c790fa
re-organize functions into separate file
haberdashPI Feb 15, 2022
2b60486
removed temporary test project files
haberdashPI Feb 15, 2022
35e60c2
fix small test error
haberdashPI Feb 15, 2022
e445a2e
remove Infiltrator
haberdashPI Feb 16, 2022
67a6010
fix typo
haberdashPI Feb 16, 2022
0bf225c
added comment
haberdashPI Mar 25, 2022
879ddfb
attempt to fix isdisjoint issues
haberdashPI Mar 26, 2022
8b0ebe0
update docstrings
haberdashPI Mar 29, 2022
c589fc4
Apply minor fixes from code review
haberdashPI May 12, 2022
2c64ea0
missing newline
haberdashPI May 12, 2022
979468f
move some documentation around
haberdashPI May 12, 2022
7cab127
resolved review comments
haberdashPI May 12, 2022
45e820b
CI fixes
haberdashPI May 12, 2022
e4c9e5a
fix missing documentation links
haberdashPI May 12, 2022
e14e521
Drop additional `in` method
omus May 13, 2022
39bb800
Scalar union test
omus May 13, 2022
e7358b4
Use mapreduce in sets tests
omus May 13, 2022
aaf15a1
Remove unnecessary union copy
omus May 13, 2022
2472621
Tweaks
omus May 13, 2022
053b10c
Refactor sets tests
omus May 13, 2022
78ba545
Use do-block in sets tests
omus May 13, 2022
5779c7a
Endpoint tracking tests
omus May 13, 2022
995f411
Add new set functions to comparison tests
omus May 14, 2022
ef4043b
Refactor issetequal comparison tests
omus May 14, 2022
a3b3e93
Remove redundant symdiff tests
omus May 14, 2022
fc6538b
Iterate on interval as sets
omus May 16, 2022
7347dbe
Drop setdiff for intervals as sets
omus May 16, 2022
fd6a3ee
Revert set changes to test/intervals.jl
omus May 16, 2022
22ccfa7
Update set tests to use vectors
omus May 16, 2022
ae12553
Restrict new set functions to vectors
omus May 16, 2022
4d5f20e
Refactor endpoint tracking
omus May 16, 2022
122ae86
Rename kwargs
omus May 16, 2022
ba01575
Drop union of single interval
omus May 16, 2022
e76884c
Drop redundant docstrings
omus May 16, 2022
856e601
Drop old cross-references
omus May 17, 2022
418cc9a
Do not export find_intersections
omus May 17, 2022
82d325c
Move plotting docs section
omus May 17, 2022
2d959ea
Add documentation on sets
omus May 17, 2022
7fd1efa
fixup! Do not export find_intersections
omus May 17, 2022
7fc6c8e
fix endpoint bug
haberdashPI May 20, 2022
dc7b62a
move to more modern test package specification
haberdashPI May 23, 2022
294d60e
fix some whitespace
haberdashPI May 23, 2022
dbc1b2c
fix some broken tests
haberdashPI May 23, 2022
28cea42
ignore test manifest
haberdashPI May 23, 2022
932b35a
Apply suggestions from code review
haberdashPI May 26, 2022
94a4430
cleanup from code review
haberdashPI May 26, 2022
525d3ae
revised some set tests
haberdashPI May 26, 2022
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
2 changes: 2 additions & 0 deletions .JuliaFormatter.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
style = "blue"
margin = 92
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
/docs/build/
/docs/site/
/Manifest.toml
/test/Manifest.toml
7 changes: 4 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ julia = "1.6"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
Infinity = "a303e19e-6eb4-11e9-3b09-cd9505f79100"
InvertedIndices = "41ab1584-1d38-5bbf-9106-f11c6c58b48f"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
VisualRegressionTests = "34922c18-7c2a-561c-bac1-01e79b2c4c92"
haberdashPI marked this conversation as resolved.
Show resolved Hide resolved

[targets]
test = ["Documenter", "ImageMagick", "Infinity", "Plots", "Test", "VisualRegressionTests"]
84 changes: 50 additions & 34 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,38 @@ This package defines:
* [`Open`](@ref), indicating the endpoint value of the interval is not included
* [`Unbounded`](@ref), indicating the endpoint value is effectively infinite

## Example Usage
## Sets

### Intersection
A single interval can be used to represent a contiguous set within a domain but cannot be
used to represent a disjoint set. Due to this restriction all set-based operations that
return an interval will always return a vector of intervals. These operations will combine
any intervals which are overlapping or touching into a single continuous interval and never
return an interval instance which itself is empty.

```jldoctest
julia> a = 1..10
Interval{Int64, Closed, Closed}(1, 10)
```julia
julia> union([1..10], [5..15])
1-element Vector{Interval{Int64, Closed, Closed}}:
Interval{Int64, Closed, Closed}(1, 15)

julia> b = 5..15
Interval{Int64, Closed, Closed}(5, 15)
julia> intersect([1..10], [5..15])
1-element Vector{Interval{Int64, Closed, Closed}}:
Interval{Int64, Closed, Closed}(5, 10)

julia> intersect(a, b)
Interval{Int64, Closed, Closed}(5, 10)
julia> setdiff([1..10], [5..15])
1-element Vector{Interval{Int64, Closed, Open}}:
Interval{Int64, Closed, Open}(1, 5)

julia> symdiff([1..10], [5..15])
2-element Vector{Interval{Int64}}:
Interval{Int64, Closed, Open}(1, 5)
Interval{Int64, Open, Closed}(10, 15)

julia> intersect([1..5], [10..15])
Interval[]
```

## Example Usage

### Bounds

```jldoctest
Expand Down Expand Up @@ -135,31 +152,6 @@ julia> anchor(he)
2013-02-13T01:00:00-06:00
```

### Plotting
`AbstractInterval` subtypes can be plotted with [Plots.jl](https://github.com/JuliaPlots/Plots.jl).


```julia
julia> using Plots

julia> start_dt = DateTime(2017,1,1,0,0,0);

julia> end_dt = DateTime(2017,1,1,10,30,0);

julia> datetimes = start_dt:Hour(1):end_dt
DateTime("2017-01-01T00:00:00"):Hour(1):DateTime("2017-01-01T10:00:00")

julia> intervals = HE.(datetimes);

julia> plot(intervals, 1:11)
```

![Example Plot](assets/HE.png)

In the plot, inclusive boundaries are marked with a vertical bar, whereas exclusive boundaries just end.



### Comparisons

#### Equality
Expand Down Expand Up @@ -247,6 +239,30 @@ julia> round(AnchoredInterval{+0.5}(0.5), on=:right)
AnchoredInterval{0.5, Float64, Closed, Open}(0.5)
```

### Plotting

`AbstractInterval` subtypes can be plotted with [Plots.jl](https://github.com/JuliaPlots/Plots.jl).

```julia
julia> using Plots

julia> start_dt = DateTime(2017,1,1,0,0,0);

julia> end_dt = DateTime(2017,1,1,10,30,0);

julia> datetimes = start_dt:Hour(1):end_dt
DateTime("2017-01-01T00:00:00"):Hour(1):DateTime("2017-01-01T10:00:00")

julia> intervals = HE.(datetimes);

julia> plot(intervals, 1:11)
```

![Example Plot](assets/HE.png)

In the plot, inclusive boundaries are marked with a vertical bar, whereas exclusive boundaries just end.


## API

```@docs
Expand Down
1 change: 1 addition & 0 deletions src/Intervals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ bounds_types(x::AbstractInterval{T,L,R}) where {T,L,R} = (L, R)
include("isfinite.jl")
include("endpoint.jl")
include("interval.jl")
include("interval_sets.jl")
include("anchoredinterval.jl")
include("parse.jl")
include("description.jl")
Expand Down
3 changes: 2 additions & 1 deletion src/endpoint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ const Right = Direction{:Right}()
const Beginning = Left
const Ending = Right

struct Endpoint{T, D, B <: Bound}
abstract type AbstractEndpoint end
struct Endpoint{T, D, B <: Bound} <: AbstractEndpoint
endpoint::T

function Endpoint{T,D,B}(ep::T) where {T, D, B <: Bounded}
Expand Down
54 changes: 0 additions & 54 deletions src/interval.jl
Original file line number Diff line number Diff line change
Expand Up @@ -405,60 +405,6 @@ function Base.intersect(a::AbstractInterval{S}, b::AbstractInterval{T}) where {S
return Interval(left, right)
end

# There is power in a union.
"""
union(intervals::AbstractVector{<:AbstractInterval})

Flattens a vector of overlapping intervals into a new, smaller vector containing only
non-overlapping intervals.
"""
function Base.union(intervals::AbstractVector{<:AbstractInterval})
return union!(convert(Vector{AbstractInterval}, intervals))
end

"""
union!(intervals::AbstractVector{<:Union{Interval, AbstractInterval}})

Flattens a vector of overlapping intervals in-place to be a smaller vector containing only
non-overlapping intervals.
"""
function Base.union!(intervals::Union{AbstractVector{<:Interval}, AbstractVector{AbstractInterval}})
sort!(intervals)

i = 2
n = length(intervals)
while i <= n
prev = intervals[i - 1]
curr = intervals[i]

# If the current and previous intervals don't meet then move along
if !overlaps(prev, curr) && !contiguous(prev, curr)
i = i + 1

# If the two intervals meet then we absorb the current interval into
# the previous one.
else
intervals[i - 1] = merge(prev, curr)
deleteat!(intervals, i)
n -= 1
end
end

return intervals
end

"""
superset(intervals::AbstractArray{<:AbstractInterval}) -> Interval

Create the smallest single interval which encompasses all of the provided intervals.
"""
function superset(intervals::AbstractArray{<:AbstractInterval})
left = minimum(LeftEndpoint.(intervals))
right = maximum(RightEndpoint.(intervals))

return Interval(left, right)
end

function Base.merge(a::AbstractInterval, b::AbstractInterval)
if !overlaps(a, b) && !contiguous(a, b)
throw(ArgumentError("$a and $b are neither overlapping or contiguous."))
Expand Down
Loading