Skip to content

Commit c8a4c28

Browse files
devmotionnalimilan
andauthored
Add logcosh (#32)
* Add `logcosh` * Extend docstring Co-authored-by: Milan Bouchet-Valat <[email protected]> Co-authored-by: Milan Bouchet-Valat <[email protected]>
1 parent c827fd3 commit c8a4c28

File tree

9 files changed

+49
-3
lines changed

9 files changed

+49
-3
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "LogExpFunctions"
22
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
33
authors = ["StatsFun.jl contributors, Tamas K. Papp <[email protected]>"]
4-
version = "0.3.5"
4+
version = "0.3.6"
55

66
[deps]
77
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"

docs/src/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ Various special functions based on `log` and `exp` moved from [StatsFuns.jl](htt
44

55
The original authors of these functions are the StatsFuns.jl contributors.
66

7-
LogExpFunctions supports [`InverseFunctions.inverse`](https://github.com/JuliaMath/InverseFunctions.jl) and [`ChangesOfVariables.test_with_logabsdet_jacobian`](https://github.com/JuliaMath/ChangesOfVariables.jl) for `log1mexp`, `log1pexp`, `log1pexp`, `log2mexp`, `log2mexp`, `logexpm1`, `logistic`, `logistic` and `logit`.
7+
LogExpFunctions supports [`InverseFunctions.inverse`](https://github.com/JuliaMath/InverseFunctions.jl) and [`ChangesOfVariables.test_with_logabsdet_jacobian`](https://github.com/JuliaMath/ChangesOfVariables.jl) for `log1mexp`, `log1pexp`, `log1pexp`, `log2mexp`, `log2mexp`, `logexpm1`, `logistic`, `logistic`, `logit`, and `logcosh` (no inverse).
88

99
```@docs
1010
xlogx
1111
xlogy
1212
xlog1py
1313
logistic
1414
logit
15+
logcosh
1516
log1psq
1617
log1pexp
1718
log1mexp

src/LogExpFunctions.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import LinearAlgebra
1111

1212
export xlogx, xlogy, xlog1py, logistic, logit, log1psq, log1pexp, log1mexp, log2mexp, logexpm1,
1313
softplus, invsoftplus, log1pmx, logmxp1, logaddexp, logsubexp, logsumexp, softmax,
14-
softmax!
14+
softmax!, logcosh
1515

1616
include("basicfuns.jl")
1717
include("logsumexp.jl")

src/basicfuns.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,18 @@ logit(x::Real) = log(x / (one(x) - x))
9494
"""
9595
$(SIGNATURES)
9696
97+
Return `log(cosh(x))`, carefully evaluated without intermediate calculation of `cosh(x)`.
98+
99+
The implementation ensures `logcosh(-x) = logcosh(x)`.
100+
"""
101+
function logcosh(x::Real)
102+
abs_x = abs(x)
103+
return abs_x + log1pexp(- 2 * abs_x) - IrrationalConstants.logtwo
104+
end
105+
106+
"""
107+
$(SIGNATURES)
108+
97109
Return `log(1+x^2)` evaluated carefully for `abs(x)` very small or very large.
98110
"""
99111
log1psq(x::Real) = log1p(abs2(x))

src/chainrules.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ ChainRulesCore.@scalar_rule(xlog1py(x::Real, y::Real), (log1p(y), x / (1 + y),))
44

55
ChainRulesCore.@scalar_rule(logistic(x::Real), (Ω * (1 - Ω),))
66
ChainRulesCore.@scalar_rule(logit(x::Real), (inv(x * (1 - x)),))
7+
ChainRulesCore.@scalar_rule(logcosh(x::Real), tanh(x))
78
ChainRulesCore.@scalar_rule(log1psq(x::Real), (2 * x / (1 + x^2),))
89
ChainRulesCore.@scalar_rule(log1pexp(x::Real), (logistic(x),))
910
ChainRulesCore.@scalar_rule(log1mexp(x::Real), (-exp(x - Ω),))

src/with_logabsdet_jacobian.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,11 @@ function ChangesOfVariables.with_logabsdet_jacobian(::typeof(logistic), x::Real)
2727
y = logistic(x)
2828
y, log(y * (1 - y))
2929
end
30+
31+
function ChangesOfVariables.with_logabsdet_jacobian(::typeof(logcosh), x::Real)
32+
abs_x = abs(x)
33+
a = - 2 * abs_x
34+
z = log1pexp(a)
35+
y = abs_x + z - IrrationalConstants.logtwo
36+
return y, log1mexp(a) - z
37+
end

test/basicfuns.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,22 @@ end
5454
@test logit(logistic(2)) 2.0
5555
end
5656

57+
@testset "logcosh" begin
58+
for x in (randn(), randn(Float32))
59+
@test @inferred(logcosh(x)) isa typeof(x)
60+
@test logcosh(x) log(cosh(x))
61+
@test logcosh(-x) == logcosh(x)
62+
end
63+
64+
# special values
65+
for x in (-Inf, Inf, -Inf32, Inf32)
66+
@test @inferred(logcosh(x)) === oftype(x, Inf)
67+
end
68+
for x in (NaN, NaN32)
69+
@test @inferred(logcosh(x)) === x
70+
end
71+
end
72+
5773
@testset "log1psq" begin
5874
@test iszero(log1psq(0.0))
5975
@test log1psq(1.0) log1p(1.0)

test/chainrules.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
test_rrule(logistic, x; rtol=1f-3, atol=1f-3)
3232
end
3333

34+
for x in (-randexp(), randexp())
35+
test_frule(logcosh, x)
36+
test_rrule(logcosh, x)
37+
end
38+
3439
# test all branches of `log1pexp`
3540
for x in (-20.9, 15.4, 41.5)
3641
test_frule(log1pexp, x)

test/with_logabsdet_jacobian.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,7 @@
1616
ChangesOfVariables.test_with_logabsdet_jacobian(logistic, x, derivative)
1717

1818
ChangesOfVariables.test_with_logabsdet_jacobian(logit, rand(), derivative)
19+
20+
ChangesOfVariables.test_with_logabsdet_jacobian(logcosh, x, derivative)
21+
ChangesOfVariables.test_with_logabsdet_jacobian(logcosh, -x, derivative)
1922
end

0 commit comments

Comments
 (0)