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

Implement hcubature_count and hcubature_print #75

Merged
merged 11 commits into from
Dec 17, 2024
47 changes: 46 additions & 1 deletion src/HCubature.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module HCubature
using StaticArrays, LinearAlgebra
import Combinatorics, DataStructures, QuadGK

export hcubature, hquadrature, hcubature_buffer
export hcubature, hquadrature, hcubature_buffer, hcubature_count, hcubature_print

include("genz-malik.jl")
include("gauss-kronrod.jl")
Expand Down Expand Up @@ -236,6 +236,51 @@ hcubature(f, a, b; norm=norm, rtol::Real=0, atol::Real=0,
hcubature_(f, a, b, norm, rtol, atol, maxevals, initdiv, buffer)


"""
hcubature_count(f, a, b; kws...)

Identical to [`hcubature`](@ref) but returns a triple `(I, E, count)`
of the estimated integral `I`, the estimated error bound `E`, and a `count`
of the number of times the integrand `f` was evaluated.

The count of integrand evaluations is a useful performance metric: a large
number typically indicates a badly behaved integrand (with singularities,
discontinuities, sharp peaks, and/or rapid oscillations), in which case
it may be possible to mathematically transform the problem in some way
to improve the convergence rate.
"""
function hcubature_count(f, a, b; kws...)
count = Ref(0)
I, E = hcubature(a, b; kws...) do x
count[] += 1
f(x)
end
return (I, E, count[])
end

"""
hcubature_print([io], f, a, b; kws...)

Identical to [`hcubature`](@ref), but **prints** each integrand
evaluation to the stream `io` (defaults to `stdout`) in the form:
```
f(x1) = y1
f(x2) = y2
...
```
which is useful for pedagogy and debugging.

Also, like [`hcubature_count`](@ref), it returns a triple `(I, E, count)`
of the estimated integral `I`, the estimated error bound `E`, and a `count`
of the number of times the integrand `f` was evaluated.
"""
hcubature_print(io::IO, f, a, b; kws...) = hcubature_count(a, b; kws...) do x
y = f(x)
println(io, "f($x) = $y")
y
end
hcubature_print(f, a, b; kws...) = hcubature_print(stdout, f, a, b; kws...)

"""
hquadrature(f, a, b; norm=norm, rtol=sqrt(eps), atol=0, maxevals=typemax(Int), initdiv=1)

Expand Down
23 changes: 23 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,29 @@ using Test
end
end

@testset "count" begin
(i, e, count) = hcubature_count(x -> 2, (0,0), (2pi, pi))
@test i ≈ 4pi^2
@test count == HCubature.countevals(HCubature.GenzMalik(Val(2)))
end

@testset "print" begin
# Capture println's in a buffer, ensure one printed line per integrand eval
let io = IOBuffer()
(i, e, count) = hcubature_print(io, x -> 2, (0,0), (2pi, pi))
regex = r"f\((?<x>.+?)\) = (?<y>.+?)"
io_lines = collect(eachmatch(regex, String(take!(io))))
@test i ≈ 4pi^2
@test length(io_lines) == count
end

# Test hcubature_print(f, a, b) without io arg specified
# Suppress output of internal printing to stdout
f() = hcubature_print(x -> 2, (0,0), (2pi, pi))
(i, e, count) = redirect_stdout(f, devnull);
@test i ≈ 4pi^2
stevengj marked this conversation as resolved.
Show resolved Hide resolved
end

# function wrapper for counting evaluations
const gcnt = Ref(0)
cnt(f) = x -> begin
Expand Down
Loading