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

[WIP] Replace method symbols with abstract types #149

Merged
merged 17 commits into from
Feb 23, 2016
Merged

[WIP] Replace method symbols with abstract types #149

merged 17 commits into from
Feb 23, 2016

Conversation

Evizero
Copy link
Contributor

@Evizero Evizero commented Oct 24, 2015

As discussed in #145 .

  • replace method symbols with abstract types to exploit type dispatch and simplify exposing custom keyword arguments for different methods
  • remove nnls
  • clean up tests to use types instead of symbols to specify the method
  • deprecate old standalone methods (e.g. Optim.l_bfgs)
  • add tests for deprecated methods (?)
  • use hashtable for method symbols
  • adapt readme to new API (thanks to @pkofod)
  • review and then resolve merge-conflicts with master

This works right now although the tests still need some clean-up.

I thought this would be a good point to gather feedback

@mlubin
Copy link
Contributor

mlubin commented Oct 26, 2015

I like it. Should we deprecate choosing the algorithms via symbols?

@gabrielgellner
Copy link

Super sweet! Should getOptimizer be get_optimizer? I thought camel case was a no no in Julia.

@Evizero
Copy link
Contributor Author

Evizero commented Oct 27, 2015

Should we deprecate choosing the algorithms via symbols?

I don't know. I simply assumed so for some reason. Personally, I'm neutral to it.

Should getOptimizer be get_optimizer? I thought camel case was a no no in Julia.

yes, good catch! old habits...

callback = nothing,
show_every = 1,
linesearch!::Function = hz_linesearch!,
nargs...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this? Let's avoid passing around things that aren't strictly necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you mean?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of nargs...?

@johnmyleswhite
Copy link
Contributor

This looks really great. Two concerns:

  • Passing around nargs is somewhat un-Julian. We shouldn't propagate things that aren't amenable to human-level static analysis.
  • We should deprecate the old functions that are being removed, like Optim.l_bfgs. Some people are almost certainly calling those functions directly.

initial_x::Array,
method::Optimizer;
autodiff::Bool = false,
nargs...)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pass around nargs... to not break this design choice using autodiff as a KW args. This is because now this method is a generic fall-back.

@Evizero
Copy link
Contributor Author

Evizero commented Oct 27, 2015

Passing around nargs is somewhat un-Julian

See my last comment. I'm not sure how to solve this without breaking the autodiff KW, but I'll look into it. (If you have an idea I'm all ears)

We should deprecate the old functions that are being removed

ok will do

@Evizero
Copy link
Contributor Author

Evizero commented Oct 28, 2015

A question about nargs...: Do you want to get rid of those in general? even in optimize.jl where they greatly reduce redundant code? If so I will. But I got to say that this would make the whole code a lot uglier and almost all default values meaningless as they would always be overwritten by the initial method. Seems like bad coding practice to me

@johnmyleswhite
Copy link
Contributor

Yes, we can't merge code that uses nargs. It makes local reasoning excessively difficult because it pushes around an unknown number of arguments of unknown types.

When I have some time free, I will figure out how to avoid the use of nargs. Ideally we'll move towards a shim-layer API that translates keyword arguments into a type that has fields for all options. You can see what NLopt does for an example: https://github.com/JuliaOpt/NLopt.jl/blob/523987afbb32105ed78dad0a127298284113147d/src/NLoptSolverInterface.jl#L4

@Evizero
Copy link
Contributor Author

Evizero commented Oct 29, 2015

When I have some time free, I will figure out how to avoid the use of nargs. Ideally we'll move towards a shim-layer API that translates keyword arguments into a type that has fields for all options

This makes sense to me. I'll give it a shot.

@Evizero
Copy link
Contributor Author

Evizero commented Oct 31, 2015

Let me know what you think of this. Basically I put the general optimization options in OptimizationOptions and the special arguments (such as the history size of LBFGS) into the Optimizer type itself

@mlubin
Copy link
Contributor

mlubin commented Oct 31, 2015

👍
Embedding the options in the algorithm types themselves is essentially what we also do in MathProgBase.

@mlubin
Copy link
Contributor

mlubin commented Dec 26, 2015

@Evizero, any plans to finish this up? It would be a very welcome contribution.

@Evizero
Copy link
Contributor Author

Evizero commented Dec 27, 2015

I got sidetracked because the approach of optimizing Julia-functions directly turned out to be somewhat inconvenient for what I was working on at the time.

Be that as it may, I do think the main bulk of work is done already. So if you're still interested in this, then I'll see to it that I finish this up within the next couple weeks. I'm currently drowning in work for an overdue project, but I do try to get a little Julia coding done each day.

@mlubin
Copy link
Contributor

mlubin commented Dec 28, 2015

Definitely interested, I promise a quick review on my part

@Evizero
Copy link
Contributor Author

Evizero commented Jan 28, 2016

Some progress. Still need to add the old methods back and deprecate them (e.g. Optim.l_bfgs(...))

@Evizero
Copy link
Contributor Author

Evizero commented Jan 28, 2016

BTW I wasn't able to rebase because of conflicting patches. So now nnls is reintroduced. If I remember it correctly I originally deleted nnls because I read somewhere that this was on the agenda anyway. EDIT: I undid the premature merge.

Should nnls stay or go?

@johnmyleswhite
Copy link
Contributor

Either is good by me if it helps you.

@pkofod
Copy link
Member

pkofod commented Feb 15, 2016

Looks good to me.

@Evizero
Copy link
Contributor Author

Evizero commented Feb 15, 2016

All right then! Tomorrow morning I will go over the code again to see if I missed something (like a test here or there) and then I'll resolve the merge conflicts with master so we can merge this!

@pkofod
Copy link
Member

pkofod commented Feb 15, 2016

Just a quick question, do we need to export Optimizer? I'm not saying it's wrong, I'm just trying to understand what the benefit is.

@Evizero
Copy link
Contributor Author

Evizero commented Feb 15, 2016

no, good point. I guess there is no benefit for the end user in having Optimizer in the namespace. I will take a close look at the exports tomorrow as well.

@Evizero
Copy link
Contributor Author

Evizero commented Feb 16, 2016

So, I think this should be ok now as far as the API is concerned. I undid some commits here and there, since the method keyword is back into play.

The travis error on 0.5 looks unrelated to this patch.

@@ -1,6 +1,14 @@

for method in (:nelder_mead,
:simulated_annealing)
function cb(tr::OptimizationTrace)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function and the one below should be removed. They got accidentally added in your rebase.

After removing those, tests pass on 0.5.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. Well If I remove those then there is no test anymore that checks if the callback got called

Maybe an anonymous function will do the trick

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am only talking about the two most upper ones. They seem completely unused since they get overwritten immediately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I saw them too and removed them.

@pkofod
Copy link
Member

pkofod commented Feb 17, 2016

Essentially, lgtm. Looking down over the README.mdagain. Now, we still allow method to be specified, but I'm not sure there's a reason to introduce the usage of optimize as if method is required. On the other hand, the documentation might need a larger overhaul soon, so this can be merged as is (imho) as the syntax still works.

@pkofod
Copy link
Member

pkofod commented Feb 22, 2016

@johnmyleswhite sorry to ping, but do you have any comments ? Besides a pre-merge squash maybe.

@johnmyleswhite
Copy link
Contributor

I will review this tonight and make sure this gets merged unless there turns out to be an issue I don't expect -- which I think is extremely unlikely.

@@ -52,36 +30,55 @@ optimize(f, [0.0, 0.0])
Optim will default to using the Nelder-Mead method in this case. We can specify the Nelder-Mead method explicitly using the `method` keyword:

```jl
optimize(f, [0.0, 0.0], method = :nelder_mead)
optimize(f, [0.0, 0.0], method = NelderMead())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we encourage people to use keywords in the docs? I would think one of the main gains of the switch to types is the chance for completely static dispatch, which I would assume want to promote, although that's obviously a complex issue.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it should go away most places, and it is my fault it is still in there. As mentioned above, in my latest comment, I figured it could get changed when some of the other API changes are made.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me.

@johnmyleswhite
Copy link
Contributor

Made some comments that could be up for discussion, but I'm happy with this and think it's ready to merge unless anyone wants to make changes.

It's an impressively thorough body of high-quality work. Well done, @Evizero.

Removed method keywords, clarified the reasons for use of user provided gradients, and clarified a dispatch comment.
@pkofod
Copy link
Member

pkofod commented Feb 23, 2016

Okay, so I created https://github.com/Evizero/Optim.jl/pull/5 . It is up to you @Evizero if you want to merge it. If you'd rather not, I'll just commit it after this merges.

I agree this is solid work, and I really think it moves the package forwards.

Fix README.md once more.
@Evizero
Copy link
Contributor Author

Evizero commented Feb 23, 2016

Thanks for the review and kind comments. I am glad that I could contribute something useful and will hopefully find myself in this position again.

mlubin added a commit that referenced this pull request Feb 23, 2016
[WIP] Replace method symbols with abstract types
@mlubin mlubin merged commit c42b60f into JuliaNLSolvers:master Feb 23, 2016
@KristofferC
Copy link
Contributor

🎉 🎈

@mlubin
Copy link
Contributor

mlubin commented Feb 23, 2016

Thanks @Evizero, this is the biggest change to Optim in quite a while.
Would someone like to summarize the changes in a post to the julia-opt list?

@pkofod
Copy link
Member

pkofod commented Feb 23, 2016

By the way, regarding the lowercase letters in Fminbox. What is one of the nice things in this PR is that if we want it to be FMinBox at some point, we can now deprecate Fminbox which was not possible with :fminbox. Very handy for potential breaking changes going forward.

Edit: We can certain notify julia-opt, but we should probably note that this is the api for optimization, and that the api for accessing the results will also change soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants