Skip to content

Conversation

@MaxenceGollier
Copy link
Contributor

#347

@amontoison @tmigot This is where I am so far.

Attached are my benchmark results...
grad_benchmarks.zip

@MaxenceGollier MaxenceGollier changed the title Type stab Type stability in ADNLPModels Jun 5, 2025
push!(args, if field in keys(kwargs) && typeof(kwargs[field]) <: ADBackend
kwargs[field]
elseif field in keys(kwargs) && typeof(kwargs[field]) <: DataType
elseif field in keys(kwargs) && typeof(kwargs[field]) <: Union{DataType, UnionAll}
Copy link
Member

Choose a reason for hiding this comment

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

@MaxenceGollier Why do you need UnionAll?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because the type ReverseDiffADGradient or ForwardDiffADGradient are now parametric types and it is no longer true that ReverseDiffADGradient <: DataType...

Copy link
Member

Choose a reason for hiding this comment

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

@tmigot Do you remember why you tested typeof(kwargs[field]) <: DataType ?
The condition typeof(kwargs[field]) <: Type is always true so I don't know why we are testing that.

Copy link
Member

Choose a reason for hiding this comment

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

Yup, it doesn't make sense as is. Maybe it should be kwargs[field] <: Type to make sure the constructor call next make sense.

Copy link
Contributor Author

@MaxenceGollier MaxenceGollier Jun 26, 2025

Choose a reason for hiding this comment

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

Why not kwargs[field] <: ADBackend ?? This seems to work locally, we are really trying to see if we can create a backend just from kwargs[field] as per the next line...
It would also make more sense with the previous check where we check if kwargs[field] is already an ADBackend by using typeof...

@amontoison
Copy link
Member

@tmigot Are you fine with the new set_adbackend ?

@tmigot
Copy link
Member

tmigot commented Jun 6, 2025

@tmigot Are you fine with the new set_adbackend ?

I will check that today

test/utils.jl Outdated
@test typeof(get_adbackend(newer_nlp).hessian_backend) <: ADNLPModels.ReverseDiffADHessian
end

function test_allocations(nlp)
Copy link
Member

Choose a reason for hiding this comment

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

Can you create an issue in NLPModelsTest.jl to add this in all models?

Copy link
Member

Choose a reason for hiding this comment

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

@MaxenceGollier can you put the link here once it's done, thanks!

push!(args, if field in keys(kwargs) && typeof(kwargs[field]) <: ADBackend
kwargs[field]
elseif field in keys(kwargs) && typeof(kwargs[field]) <: DataType
elseif field in keys(kwargs) && typeof(kwargs[field]) <: Union{DataType, UnionAll}
Copy link
Member

Choose a reason for hiding this comment

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

Yup, it doesn't make sense as is. Maybe it should be kwargs[field] <: Type to make sure the constructor call next make sense.

@tmigot
Copy link
Member

tmigot commented Jun 7, 2025

Why the tests are failing? Is it just because NLS grad! function uses other function behind and should not be tested or is there some typing issue in NLPModels?

Your benchmarks are indeed surprising @MaxenceGollier , what were the tests problems?

@tmigot
Copy link
Member

tmigot commented Jun 7, 2025

@MaxenceGollier @amontoison I moved the PR from draft to review to run the package benchmark by curiosity

@tmigot tmigot added the do not merge This is an experiment or work in progress label Jun 7, 2025
@MaxenceGollier
Copy link
Contributor Author

Why the tests are failing? Is it just because NLS grad! function uses other function behind and should not be tested or is there some typing issue in NLPModels?

That's what I have been trying to figure out, some Hessian (with JET I mean) tests also seem to fail for some reason so there might be other unstabilities hidden in this repo...

what were the tests problems?

What do you mean ? as I said, I still have to figure things out, I have a lot of urgent work to do on something else next week, I will be available to work this out the week after that...

Can you create an issue in NLPModelsTest.jl to add this in all models?

Yes good idea, I found similar issues on QuadraticModels.jl... (still need to open an issue but I don't have much time currently)

@MaxenceGollier
Copy link
Contributor Author

What do you think of this version of set_adbackend ? @amontoison @tmigot. I tried to implement all your suggestions

@MaxenceGollier
Copy link
Contributor Author

I think tests keep failing due to inherent type instabilities in ForwardDiff.jl. See in ForwardDiff/docs/advanced.md :

If your input dimension is constant across calls, you should explicitly select a chunk size rather than relying on ForwardDiff's heuristic. There are two reasons for this. The first is that ForwardDiff's heuristic depends only on the input dimension, whereas in reality the optimal chunk size will also depend on the target function. The second is that ForwardDiff's heuristic is inherently type-unstable, which can cause the entire call to be type-unstable.

The remaining type instabilities should be solved by having a closer look to those of ForwardDiff.

@amontoison amontoison requested a review from tmigot June 29, 2025 22:42
@amontoison
Copy link
Member

@tmigot
Can you take care of the review? I am still traveling this week and won't be able to have a look before 2 weeks at least.

Copy link
Member

@tmigot tmigot left a comment

Choose a reason for hiding this comment

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

Thanks a lot @MaxenceGollier for the great work! I made some comments, but we are going in a good direction.

Comment on lines 277 to 291
function ADNLPModel(nlp::ADNLPModel, new_adbackend::ADModelBackend)
return _set_adbackend(nlp, new_adbackend)
end

function ADNLPModel(nlp::ADNLPModel; kwargs...)
return _set_adbackend(nlp; kwargs...)
end

function ADNLSModel(nlp::ADNLSModel; kwargs...)
return _set_adbackend(nlp; kwargs...)
end

function ADNLSModel(nlp::ADNLSModel, new_adbackend::ADModelBackend)
return _set_adbackend(nlp, new_adbackend)
end
Copy link
Member

Choose a reason for hiding this comment

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

Good idea, but it doesn't work with this

function ADNLPModel!(model::AbstractNLPModel; kwargs...)
.
Because, we are already building mixed models.

Do we really need the ADNLPModel(nlp::ADNLPModel; kwargs...) and ADNLSModel(nlp::ADNLSModel; kwargs...) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No perhaps we don't.
We need to remove the test_getter_setter test then which basically test these functions (these are causing the tests to fail on my branch).

I am not sure though if it is a good idea to remove the functions. Let me know what you think. I we do remove them, I will juste remove the test_getter_setter thing and tests should pass.

@tmigot
Copy link
Member

tmigot commented Jun 30, 2025

I think tests keep failing due to inherent type instabilities in ForwardDiff.jl. See in ForwardDiff/docs/advanced.md :

If your input dimension is constant across calls, you should explicitly select a chunk size rather than relying on ForwardDiff's heuristic. There are two reasons for this. The first is that ForwardDiff's heuristic depends only on the input dimension, whereas in reality the optimal chunk size will also depend on the target function. The second is that ForwardDiff's heuristic is inherently type-unstable, which can cause the entire call to be type-unstable.

The remaining type instabilities should be solved by having a closer look to those of ForwardDiff.

I am unfortunately not too surprised by this. Thanks to your analysis we will improve our part of the code.
We should try to think of a way to still test that our code is good.

@amontoison
Copy link
Member

bump

@MaxenceGollier
Copy link
Contributor Author

I am unfortunately not too surprised by this. Thanks to your analysis we will improve our part of the code. We should try to think of a way to still test that our code is good.

I agree. Perhaps the best we can do is to call the report_opt macro but let it ignore all the issues from the ForwardDiff package.
If our code is stable with all other backends then we'd consider that our code is good enough.

@MaxenceGollier
Copy link
Contributor Author

MaxenceGollier commented Sep 15, 2025

I think this is ready for another round of review @tmigot @amontoison.
FreeBSD fails because of timeout. And i don't know how to run the benchmarks in GitHub...

@amontoison
Copy link
Member

@MaxenceGollier Can you rebase your branch and fix the CI tests?

@MaxenceGollier
Copy link
Contributor Author

@amontoison FreeBSD fails because we should use JET@v0.10 if we use Julia 1.12 and @v0.9 for Julia < 1.12. Do you know how we could do this well ?

@amontoison
Copy link
Member

@amontoison FreeBSD fails because we should use JET@v0.10 if we use Julia 1.12 and @v0.9 for Julia < 1.12. Do you know how we could do this well ?

I update the compat entry to support both JET 0.9, 0.10.

@MaxenceGollier
Copy link
Contributor Author

MaxenceGollier commented Oct 16, 2025

Thank you @amontoison, I need to update the doc now.
I would like to see the benchmarks ran by GitHub before i do that how can i get them to run ?

@amontoison
Copy link
Member

We need to open another PR to fix the benchmarks with Julia 1.12.
Once it is merged, we can rebase this branch on top of it.
I can't have a look before Sunday / next week if you don't do it before.

Copy link
Member

@tmigot tmigot left a comment

Choose a reason for hiding this comment

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

@amontoison
Copy link
Member

amontoison commented Oct 21, 2025

@tmigot We should be consistent for Maxence. I think a non-inplace set_backend is still relevant and it can return a new ADNLPModel.

@tmigot
Copy link
Member

tmigot commented Oct 21, 2025

@tmigot We should be consistent for Maxence and don't give contrary comments. I think a non-inplace set_backend is still relevant and return a new ADNLPModel.

Do we plan to add the new set_adbackend in this PR?

@amontoison
Copy link
Member

@tmigot We should be consistent for Maxence and don't give contrary comments. I think a non-inplace set_backend is still relevant and return a new ADNLPModel.

Do we plan to add the new set_adbackend in this PR?

We can just rename set_adbackend! as set_adbackend and specify that we return a new ADNLPModels where we "recycle" the backends + setup the new one instead of updating the current ADNLPModels. It should be quite minor modifications in the docstring and code.

What do you think?

@tmigot
Copy link
Member

tmigot commented Oct 21, 2025

Sounds, good.

@MaxenceGollier
Copy link
Contributor Author

MaxenceGollier commented Oct 22, 2025

Big GitHub noob question before moving on: where can i see the benchmark results posted ?

Shouldn't it be posted as a comment here ?

@amontoison
Copy link
Member

A rebase was needed for sure, let's see if doing a PR from a fork is also an issue.
I fixed a few CI errors, reintroduced the set_adbackend and updated the related documentation.
When the PR will be ready, I will ask Maxence to get ownership of the commits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants