-
Notifications
You must be signed in to change notification settings - Fork 66
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
New NLsolve #125
New NLsolve #125
Conversation
Wow! This is a very large effort. I don't think I will have the time in the coming days to do a full review by myself. @KristofferC would you be able to help with the review. Maybe @ChrisRackauckas also? |
This sounds perfect. I'll review it soon. |
README.md
Outdated
@@ -87,19 +87,19 @@ nlsolve(f!, initial_x) | |||
``` | |||
|
|||
Alternatively, you can construct an object of type | |||
`DifferentiableMultivariateFunction` and pass it to `nlsolve`, as in: | |||
`DifferentiableVector` and pass it to `nlsolve`, as in: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this name? It's supposed to be a function-ish object still?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, in Optim these are just called OnceDifferentiable
for example. I think the Function
makes in unnecessarily long to write. Here in NLsolve i figured... I'm not sure what I figure, I just found the "Multivariate" terminologically wrong (it's multivalued if anything). Could just call it Differentiable? I don't know. Happy to bikeshed.
README.md
Outdated
nlsolve(df, initial_x) | ||
``` | ||
|
||
If your function `f!` operates not on `AbstractVector` but on arbitrary | ||
`AbstractArray` you have to specify `initial_x` in the constructor of the | ||
`DifferentiableMultivariateFunction`: | ||
`DifferentiableVector`: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this makes it a confusing name. So it can handle non-vector arrays?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get your point, requesting bikeshed backup immediately ;)
README.md
Outdated
@@ -116,90 +116,90 @@ nlsolve(f!, initial_x, autodiff = true) | |||
### Jacobian available | |||
|
|||
If, in addition to `f!(x::AbstractVector, fx::AbstractVector)`, you have a | |||
function `g!(x::AbstractVector, gx::AbstractMatrix)` for computing the Jacobian | |||
function `j!(x::AbstractVector, gx::AbstractMatrix)` for computing the Jacobian |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 I like this semantic change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see I missed gx
in the positional argument in the README, but yeah, g
was always "confusing" to me.
src/NLsolve.jl
Outdated
include("autodiff.jl") | ||
include("mcp.jl") | ||
df_path = "differentiable_vectors/" | ||
include(df_path*"differentiable_functions.jl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use joinpath
instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rather just have inlucde("path/file.jl")
. That way static analyzers (like the VSCode plugin) can follow the include. It is not powerful enough to "constant propagate" joinpaths
(yet).
end | ||
finite_difference_jacobian!(f, x, fx, jx, autodiff) | ||
end | ||
function j!(jx, x) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If they aren't going to be handled now, you should open an issue about handling these allocations better. Is there a reason you don't just pre-allocate and enclose the fx
here instead of getting a new one every time??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't really look at this code besides renaming stuff an making sure it didn't work. I was waiting for DiffEqDiffTools to clean this up.
function fj!(fx, jx, x) | ||
f!(fx, x) | ||
function f(x) | ||
fx = similar(x) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pre-allocate an enclose?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see above
Do you have a test for something like a BandedMatrix Jacobian? |
I should add one. |
The reason for |
Yes Edit: to be a bit more specific: you just pass it using the J keyword |
Instead of |
A simple (very simply) example showing that BandedMatrices work (after waiting forty five hours to precompile BandedMatrices) julia> function f!(fstor, x)
fstor.=(1/3).*x.^3
end
f! (generic function with 1 method)
julia> function j!(jstor, x)
for i = 1:3
jstor[i,i] = x[i]^2
end
end
j! (generic function with 1 method)
julia> using NLsolve, BandedMatrices
julia> dv = DifferentiableVector(f!, j!, ones(3); J = bones(Float64, 3, 0, 0))
NLsolve.DifferentiableVector{#f!,#j!,NLsolve.#fj!#10{#f!,#j!},Array{Float64,1},BandedMatrices.BandedMatrix{Float64}}(f!, j!, NLsolve.fj!, [6.93768e-310, 6.9377e-310, 6.9377e-310], 3×3 BandedMatrices.BandedMatrix{Float64}:
nl 1.0
1.0
1.0, s[0], [0])
julia> NLsolve.jacobian(dv)
3×3 BandedMatrices.BandedMatrix{Float64}:
1.0
1.0
1.0
julia> nlsolve(dv, ones(3))
Results of Nonlinear Solver Algorithm
* Algorithm: Trust-region with dogleg and autoscaling
* Starting Point: [1.0, 1.0, 1.0]
* Zero: [0.00228366, 0.00228366, 0.00228366]
* Inf-norm of residuals: 0.000000
* Iterations: 15
* Convergence: true
* |x - x'| < 0.0e+00: false
* |f(x)| < 1.0e-08: true
* Function Calls (f): 16
* Jacobian Calls (df/dx): 16
julia> NLsolve.jacobian(dv)
3×3 BandedMatrices.BandedMatrix{Float64}:
5.2151e-6
5.2151e-6
5.2151e-6 |
JuliaLinearAlgebra/BandedMatrices.jl#39 As of yesterday it was a bad idea lol |
So, since
Happy to take suggestions. |
Is there a reason not to use the exact same function types that are used in Optim? Edit: Because they assume to return scalars? |
Something like that makes is a bit difficult to pull off, but I guess it is doable if we (as I asked a bit about the other day on gitter) separate the cache variables from these types. Then |
It would requite some uniform way of specifying the Jacobian/gradient cache type (what's a good keyword name here?) |
@KristofferC @ChrisRackauckas you both have reviewer's debt here, so I'm pinging you. What's your take on simply calling it OnceDifferentiable like in NLSolversBase, and for now you would simply have to specify a namespace/module if you load both? using NLsolve
f! ...
j! ...
x0 ...
od = OnceDifferentiable(f!, j!, ...)
nlsolve(od, x0)
using Optim
# queue the warning
f = ...
g! = ...
optod = Optim.OnceDifferentiable(f, g!, ...)
optx0 = ...
optimize(optod, x0, ...) Then I can work on merging the two approaches at a later point (and I think I know what I want to do) |
Why not go straight to this being the common approach for both, i.e. the new OnceDifferentiatible? |
sorry, but what is "this" here? :) |
You mean fix NLSolversBase and Optim at the same time? |
Would you be able to use this exact same type with NLsolve? What would need to be changed? Edit: I meant Optim. |
Some of the logic in NLSolversBase would be moved to Optim so as to make OnceDifferentiable more generic. Some of the "logic" I've baked into the type currently, would then be handled using a separate cache for Optim and one for NLsolve. This cache could then be made manually, or would be appropriately constructed based on the call being an |
I think you should go straight to an architecture for the cache that works for both then, knowing that the soon after goal is to have both use the same type. |
I may have asked this before on gitter, but would OnceDifferentiable(f, g!; derivative = ...) # Optim
OnceDifferentiable(f!, j!; derivative = ...) # NLsolve make sense if
It could also just be OnceDifferentiable(f, g!; df = ...)
OnceDifferentiable(f!, g!; df= ...) That's much more concise as well. Neither of them incorporates "partial" ( or ∂ 😮 ) as I think it gets too long, so if the latter is preferred we'll have to settle with |
Okay, pushed a big update that works with JuliaNLSolvers/NLSolversBase.jl#27 . We no longer have the objective type in this package and that gives us a net loss of LOC currently. All tests pass locally although some tests have been turned off for now. Quite a few changes but I believe that
|
@@ -13,7 +13,7 @@ function OnceDifferentiable(f!, F::AbstractArray, x::AbstractArray, autodiff = : | |||
F = similar(x) | |||
fj!(F, J, x) | |||
end | |||
return OnceDifferentiable(f!, j!, fj!, similar(x), similar(x)) | |||
return OnceDifferentiable(f!, j!, fj!, x, x) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it fine that this aliases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lovely! |
This pr closes #110 (now or never) and at the same time allows for fewer allocations in repeated calls to
nlsolve
without going all the way. That will come later (soon).The Jacobian (and "fvec" or
F(x)
) is now indf
, so non-by-n
array is allocated each time. This also means that there is no need forDifferentiableSparseMultivariateFunction
andDifferentiableGivenSparseMultivariateFunction
(... kind of a mouthful), asdf
is simply parameterized by the type of the Jacobian. The name for the objective type has changed as well toDifferentiableVector
.The choice of keeping the Jacobian and
F(x)
vector indf
means that it has to be seeded, so it complicates matters a bit. I think it's worth it to avoid theSparse
andGivenSparse
types, though.I plan to major tag and put boundaries on dependent packages, and do a small write up on transitioning from one version to the other.
Up next: choose linear solver, dispatch based choice of method, even fewer allocations, docs, more.