Skip to content

Commit feac229

Browse files
authored
move from Dual and HyperDualNumbers to ForwardDiff (#8)
move from Dual and HyperDualNumbers to ForwardDiff * add windows in travis * remove FowardDiff from extras * travis to allow failure * cleaned up
1 parent 88d97fa commit feac229

7 files changed

+34
-102
lines changed

.appveyor.yml

-42
This file was deleted.

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
Manifest.toml
22
docs/build/
3+
*html

.travis.yml

+7-15
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,24 @@ language: julia
33
os:
44
- osx
55
- linux
6+
- windows
67

78
julia:
8-
- 1.0
9+
- 1
10+
- 1.3
911
- nightly
1012

11-
# Uncomment the following lines to allow failures on nightly julia
12-
# (tests will run but not make your overall status red)
13-
matrix:
14-
allow_failures:
15-
- julia: nightly
16-
1713
notifications:
1814
email: false
1915

20-
#script: # the default script is equivalent to the following
21-
# - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
22-
# - julia -e 'Pkg.clone(pwd()); Pkg.build("F1Method"); Pkg.test("F1Method"; coverage=true)';
23-
24-
after_success:
25-
- julia -e 'using Pkg; cd(Pkg.dir("F1Method")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())';
26-
- julia -e 'using Pkg; cd(Pkg.dir("F1Method")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())';
16+
codecov: true
2717

2818
jobs:
19+
allow_failures:
20+
- julia: nightly
2921
include:
3022
- stage: "Documentation"
31-
julia: 1.0
23+
julia: 1.3
3224
os: linux
3325
script:
3426
- julia --project=docs/ -e 'using Pkg; Pkg.instantiate();

Project.toml

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
name = "F1Method"
22
uuid = "d5605bae-6d76-11e9-2fd7-71bcf42edbaa"
33
authors = ["Benoit Pasquier <pasquieb@uci.edu>"]
4-
version = "0.2.1"
4+
version = "0.3"
55

66
[deps]
77
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
8-
DualNumbers = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74"
9-
HyperDualNumbers = "50ceba7f-c3ee-5a84-a6e8-3ad40456ec97"
8+
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
109
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1110

1211
[compat]
12+
DiffEqBase = "6"
13+
ForwardDiff = "0.10"
1314
julia = "1.0"
1415

1516
[extras]
16-
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
1717
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1818

1919
[targets]
20-
test = ["Test", "ForwardDiff"]
20+
test = ["Test"]

README.md

+11-21
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,27 @@
33

44
# F-1 algorithm
55

6-
<p>
7-
<a href="https://doi.org/10.5281/zenodo.2667835">
8-
<img src="https://zenodo.org/badge/DOI/10.5281/zenodo.2667835.svg" alt="DOI">
9-
</a>
10-
<a href="https://github.com/briochemc/F1Method.jl/blob/master/LICENSE">
11-
<img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-yellow.svg">
12-
</a>
13-
</p>
146
<p>
157
<a href="https://briochemc.github.io/F1Method.jl/stable/">
16-
<img src=https://img.shields.io/badge/docs-stable-blue.svg>
17-
</a>
18-
<a href="https://briochemc.github.io/F1Method.jl/latest/">
19-
<img src=https://img.shields.io/badge/docs-dev-blue.svg>
8+
<img src=https://img.shields.io/badge/docs-stable-important.svg?style=flat-square&label=Documentation&logo=Read%20the%20Docs>
209
</a>
2110
</p>
11+
2212
<p>
23-
<a href="https://travis-ci.com/briochemc/F1Method.jl">
24-
<img alt="Build Status" src="https://travis-ci.com/briochemc/F1Method.jl.svg?branch=master">
13+
<a href="https://doi.org/10.5281/zenodo.2667835">
14+
<img src="http://img.shields.io/badge/DOI-10.5281%20%2F%20zenodo.2667835-blue.svg?&style=flat-square">
2515
</a>
26-
<a href='https://coveralls.io/github/briochemc/F1Method.jl'>
27-
<img src='https://coveralls.io/repos/github/briochemc/F1Method.jl/badge.svg' alt='Coverage Status' />
16+
<a href="https://github.com/briochemc/F1Method.jl/blob/master/LICENSE">
17+
<img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg?&style=flat-square">
2818
</a>
2919
</p>
20+
3021
<p>
31-
<a href="https://ci.appveyor.com/project/briochemc/f1method-jl">
32-
<img alt="Build Status" src="https://ci.appveyor.com/api/projects/status/prm2xfd6q5pba1om?svg=true">
22+
<a href="https://travis-ci.com/briochemc/F1Method.jl">
23+
<img alt="Build Status" src="https://img.shields.io/travis/com/briochemc/F1Method.jl/master?label=OSX/Linux/Windows&logo=travis&logocolor=white&style=flat-square">
3324
</a>
3425
<a href="https://codecov.io/gh/briochemc/F1Method.jl">
35-
<img src="https://codecov.io/gh/briochemc/F1Method.jl/branch/master/graph/badge.svg" />
26+
<img src="https://img.shields.io/codecov/c/github/briochemc/F1Method.jl/master?label=Codecov&logo=codecov&logoColor=white&style=flat-square">
3627
</a>
3728
</p>
3829

@@ -62,8 +53,7 @@ The F-1 algorithm is **easy** to use, gives **accurate** results, and is computa
6253

6354
- **Easy** — The F-1 algorithm basically just needs the user to provide a solver (for finding the steady-state), the mismatch function, `f`, the state function, `F`, and their derivatives, `∇ₓf` and `∇ₓF` w.r.t. the state `x`.
6455
(Note these derivatives can be computed numerically, via the [ForwardDiff](https://github.com/JuliaDiff/ForwardDiff.jl) package for example.)
65-
- **Accurate** — Thanks to dual and hyperdual numbers, the accuracy of the gradient and Hessian, as computed by the F-1 algorithm, are close to machine precision.
66-
(The F-1 algorithm uses the [DualNumbers](https://github.com/JuliaDiff/DualNumbers.jl) and [HyperDualNumbers](https://github.com/JuliaDiff/HyperDualNumbers.jl) packages.)
56+
- **Accurate** — Thanks to ForwardDiff's nested dual numbers implementation, the accuracy of the gradient and Hessian, as computed by the F-1 algorithm, are close to machine precision.
6757
- **Fast** — The F-1 algorithm is as fast as if you derived analytical formulas for every first and second derivatives *and* used those in the most efficient way.
6858
This is because the bottleneck of such computations is the number of matrix factorizations, and the F-1 algorithm only requires a single one. In comparison, standard autodifferentiation methods that take the steady-state solver as a black box would require order `m` or `m^2` factorizations, where `m` is the number of parameters.
6959

docs/Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
44
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
55

66
[compat]
7-
Documenter = "~0.22"
7+
Documenter = "~0.24"

src/F1Method.jl

+9-18
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ refer to the Equation numbers in the above manuscript. A bibtex
77
citation file is available in the GitHub repository.
88
======================================================================#
99

10-
using LinearAlgebra, DualNumbers, HyperDualNumbers, DiffEqBase
10+
using LinearAlgebra, ForwardDiff, DiffEqBase
1111

1212
"""
1313
Mem
@@ -21,21 +21,22 @@ Contains
2121
- `p` the parameters 𝒑
2222
The `Mem`-type object should be initialized with `initialize_mem`.
2323
"""
24-
mutable struct Mem
24+
mutable struct Mem
2525
s # 𝒔(𝒑)
2626
A # factors of 𝐀 = ∇ₓ𝑭(𝒔,𝒑)
2727
∇s # ∇𝒔(𝒑)
2828
∇ₓf # ∇ₓ𝑓(𝒔,𝒑)
2929
p # 𝒑
3030
end
3131

32+
3233
function update_mem!(f, F, ∇ₓf, ∇ₓF, mem, p, alg; options...)
3334
if p mem.p # only update mem if 𝒑 has changed
3435
update_solution!(F, ∇ₓF, mem, p, alg; options...)
3536
s, m = mem.s.u, length(p)
36-
∇ₚF = reduce(hcat, [𝔇(F(s, p + ε * e(j,m))) for j in 1:m]) # (2.7)
37+
∇ₚF = ForwardDiff.jacobian(p -> F(s,p), p)
3738
mem.A = factorize(∇ₓF(s,p)) # update factors of ∇ₓ𝑭(𝒔,𝒑)
38-
mem.∇s .= mem.A \ -∇ₚF # update ∇𝒔 (2.2)
39+
mem.∇s .= mem.A \ -∇ₚF # update ∇𝒔
3940
mem.∇ₓf .= ∇ₓf(s,p) # update ∇ₓ𝑓(𝒔,𝒑)
4041
mem.p = p # update 𝒑
4142
end
@@ -73,8 +74,8 @@ Returns the gradient of the `objective` function using the F-1 method.
7374
function gradient(f, F, ∇ₓf, ∇ₓF, mem, p, alg; options...)
7475
update_mem!(f, F, ∇ₓf, ∇ₓF, mem, p, alg; options...)
7576
s, ∇s, m = mem.s, mem.∇s, length(p)
76-
∇ₚf = [𝔇(f(s,p + ε * e(j,m))) for j in 1:m]' # (2.6)
77-
return mem.∇ₓf * ∇s + ∇ₚf # (2.1)
77+
∇ₚf = ForwardDiff.jacobian(p -> [f(s,p)], p)
78+
return mem.∇ₓf * ∇s + ∇ₚf
7879
end
7980

8081
"""
@@ -86,14 +87,8 @@ function hessian(f, F, ∇ₓf, ∇ₓF, mem, p, alg; options...)
8687
update_mem!(f, F, ∇ₓf, ∇ₓF, mem, p, alg; options...)
8788
s, A, ∇s, m = mem.s, mem.A, mem.∇s, length(p)
8889
A⁻ᵀ∇ₓfᵀ = vec(A' \ mem.∇ₓf') # independent of (𝑗,𝑘)
89-
H, xⱼₖ = zeros(m,m), Vector{Hyper{Float64}}(undef, length(s))
90-
for j in 1:m, k in j:m # loop upper triangle (symmetry)
91-
pⱼₖ = p + ε₁ * e(j,m) + ε₂ * e(k,m) # hyperdual 𝒑
92-
@views xⱼₖ .= s + ε₁ * ∇s[:,j] + ε₂ * ∇s[:,k] # hyperdual 𝒙
93-
H[j,k] = (f(xⱼₖ,pⱼₖ)) - (F(xⱼₖ,pⱼₖ))' * A⁻ᵀ∇ₓfᵀ # (2.8)
94-
j k ? H[k,j] = H[j,k] : nothing # Hessian symmetry
95-
end
96-
return H
90+
H(λ) = f(s+∇s*λ, p+λ) - F(s+∇s*λ, p+λ)' * A⁻ᵀ∇ₓfᵀ
91+
return ForwardDiff.hessian(H, zeros(m))
9792
end
9893

9994
"""
@@ -106,8 +101,4 @@ function initialize_mem(x, p)
106101
return Mem(copy(x), nothing, zeros(n,m), zeros(1,n), nothing)
107102
end
108103

109-
e(j, m) = [i == j for i in 1:m] # 𝑗th basis vector of ℝᵐ
110-
𝔇(x) = DualNumbers.dualpart.(x) # dual part
111-
(x) = HyperDualNumbers.ε₁ε₂part.(x) # hyperdual part
112-
113104
end

0 commit comments

Comments
 (0)