Skip to content

Commit 5679875

Browse files
committed
feat: add cadence methods
1 parent a7b81fb commit 5679875

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

benchmark/cadence.jl

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using Chairmarks
2+
using Dates
3+
using SpaceDataModel.Times
4+
5+
function slow_resolution(times; rtol = 1.0e-3, check = true)
6+
dts = diff(times)
7+
check && (all(d -> (d, dts[1]; rtol), dts) || error("Data is not approximately uniformly sampled."))
8+
return dts[1]
9+
end
10+
11+
# Generate large approximately sampled times with small noise
12+
function generate_approx_times(n, dt, noise_level = 1.0e-5)
13+
base = range(0.0, step = dt, length = n)
14+
return collect(base) .+ noise_level .* randn(n)
15+
end
16+
17+
function generate_approx_times2(n, dt, noise_level = 1.0e-5)
18+
base = range(0.0, step = dt, length = n)
19+
return 1.0e9 .* Nanosecond.(collect(base)) .+ round.(Int64, noise_level .* randn(n) .* 1.0e9) .* Nanosecond(1)
20+
end
21+
22+
# Benchmark suite
23+
function run_resolution_benchmarks()
24+
sizes = [10^3, 10^4, 10^5, 10^6]
25+
dt = 1.0
26+
println("Resolution Benchmark")
27+
println("="^60)
28+
29+
for n in sizes
30+
times_range = range(0.0, step = dt, length = n)
31+
for f in [generate_approx_times, generate_approx_times2]
32+
times = f(n, dt)
33+
b_range = @b cadence($times_range) samples = 10 evals = 3
34+
b_check = @b cadence($times; check = true) samples = 10 evals = 3
35+
b_check2 = @b slow_resolution($times; check = true) samples = 10 evals = 3
36+
println("\nn = $(n):")
37+
@info "b_check" b_check b_check2 b_range
38+
end
39+
end
40+
41+
return
42+
end
43+
44+
45+
run_resolution_benchmarks()

src/SpaceDataModel.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,7 @@ include("workload.jl")
2727

2828
include("variable_interface.jl")
2929

30+
include("times.jl")
31+
using .Times
32+
3033
end

src/times.jl

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Reference: https://github.com/JuliaAPlavin/DateFormats.jl
2+
module Times
3+
using Dates
4+
using Dates: AbstractTime
5+
export , cadence
6+
7+
/ₜ(x, n) = x / n
8+
/ₜ(x::AbstractTime, n) = Nanosecond(round(Int64, Dates.tons(x) / n))
9+
10+
# workaround for `no method matching isapprox(::Nanosecond, ::Nanosecond)`
11+
(x, y; kw...) = isapprox(x, y; kw...)
12+
(x::AbstractTime, y; kw...) = isapprox(Dates.tons(x), Dates.tons(y); kw...)
13+
14+
"""
15+
cadence(times; rtol=1.0e-3, check=true)
16+
cadence(T<:Real, times; rtol=1.0e-3, check=true)
17+
18+
Return the time step of uniformly sampled `times`.
19+
20+
If `check=true`, validates that samples are approximately uniform within `rtol`.
21+
Pass a type `T<:Real` to return the cadence in seconds as that type.
22+
"""
23+
cadence(times::AbstractRange) = step(times)
24+
function cadence(times; rtol = 1.0e-3, check = true)
25+
N = length(times)
26+
@assert N > 1
27+
dt0 = /ₜ(times[N] - times[1], N - 1)
28+
check && @inbounds for i in 1:(N - 1)
29+
dt = times[i + 1] - times[i]
30+
@assert (dt, dt0; rtol) "Data is not approximately uniformly sampled."
31+
end
32+
return dt0
33+
end
34+
function cadence(T::Type{<:Real}, times; kw...)
35+
dt = cadence(times; kw...)
36+
return dt isa AbstractTime ? T(Dates.tons(dt) / 1.0e9) : T(dt)
37+
end
38+
end

test/times.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
@testitem "cadence" begin
2+
using Dates
3+
using SpaceDataModel: cadence,
4+
5+
# AbstractRange - returns step directly
6+
@test cadence(1:10) == 1
7+
@test cadence(0.0:0.5:10.0) == 0.5
8+
@test cadence(Nanosecond(1):Nanosecond(1):Nanosecond(100)) == Nanosecond(1)
9+
10+
# Vector{Float64}
11+
times = collect(0.0:1.0:100.0)
12+
@test cadence(times) == 1.0
13+
@test cadence(Float64, times) == 1.0
14+
15+
# Approximately uniform sampling
16+
times_noisy = times .+ 1.0e-6 .* randn(length(times))
17+
@test (cadence(times_noisy), 1.0; rtol = 1.0e-3)
18+
19+
# Non-uniform sampling should error
20+
times_bad = [0.0, 1.0, 3.0, 4.0]
21+
@test_throws AssertionError cadence(times_bad)
22+
@test cadence(times_bad; check = false) 4.0 / 3
23+
24+
# Dates support
25+
dt_range = DateTime(2020):Hour(1):DateTime(2020, 1, 2)
26+
@test cadence(dt_range) == Hour(1)
27+
28+
ns_times = Nanosecond.(0:1_000_000_000:10_000_000_000)
29+
@test cadence(ns_times) == Nanosecond(1_000_000_000)
30+
@test cadence(Float64, ns_times) == 1.0
31+
end

0 commit comments

Comments
 (0)