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

Objectives penalizing e.g. the amplitude of the pulse #15

Open
dkweiss31 opened this issue Sep 24, 2022 · 11 comments
Open

Objectives penalizing e.g. the amplitude of the pulse #15

dkweiss31 opened this issue Sep 24, 2022 · 11 comments

Comments

@dkweiss31
Copy link

Hello, I have been really enjoying this package! I have what is probably a quick question. In addition to Objectives that ensure that the initial states reach the target states, I'd also like to use objectives that penalize the amplitude of the pulse, or penalize deviations of the pulse from zero amplitude at the beginning or the end, etc. I didn't see an example of such an Objective in your examples, and it wasn't immediately obvious to me how it would be implemented (fwiw I'm more interested in GRAPE than Krotov). Thanks!

@goerz
Copy link
Member

goerz commented Sep 24, 2022

Are you looking for running costs, or amplitude constraints?

As for amplitude constraints, the only thing that's currently implemented is Pulse Parametrization for Krotov. That exact same feature should also be implemented for GRAPE at some point, but I haven't gotten around to that. Those constraints are just for the maximum/minimum amplitude, and aren't time-dependent (so enforcing ϵ(t) to be zero at t=0 or T=0 only wouldn't really work with that)

Specifically for GRAPE, the underlying L-BFGS-B optimizer supports bounds on the amplitudes (that's what the -B stands for). I still have to implement an interface for specifying such constraints and forward them to the optimizer, but that shouldn't be too difficult. These constraints are time-dependent, so they can be used to enforce boundary conditions in principle. But you'd set only the initial and final time step to zero, so that's a little different than the "update shape" S(t) in Krotov which also ensures smooth switch-on/-off. I'm not sure how well the hard bounds in L-BFGS-B will work to enforce boundary conditions

A third option that I've been thinking about would be to have a custom type for the controls, so instead of a Function ϵ(t) you'd have a ShapedControl object (or something like that) that wraps around two functions ϵ(t) and S(t) and uses S(t)⋅ϵ(t) as the actual physical control for propagation, while only optimizing ϵ(t) and leaving S(t) unchanged. That's basically the way to get the functionality of S(t) from Krotov into GRAPE. You could implement that "by hand" yourself by implementing all the required methods, but I've been considering adding something like that to QuantumControl.jl as a built-in feature.

As for running costs, that's also on the TODO list for GRAPE. Those wouldn't allow you to set hard limits on the amplitude though, just generally penalize larger amplitudes (which probably works fine in practice). It also wouldn't allow you to enforce boundary conditions, although it would allow you to enforce smoothness, so in combination with the L-BFGS-B bounds that might work quite well.

Let me know which of these you'd want to have prioritized, and maybe I can whip up a rough PR on a relatively short time scale.

@dkweiss31
Copy link
Author

Thanks for the speedy reply! I'd be most interested in the last two options you mentioned. The ShapedControl object would be particularly useful, since that would essentially give me the smooth turn on and turn off for free. I would also be interested in the running costs feature though, which is more along the lines of what I was originally asking about and is probably more straightforward to implement?

@goerz
Copy link
Member

goerz commented Sep 26, 2022

You could implement that "by hand" yourself by implementing all the required methods

Actually, having an S(t) that is separate from the controls makes the Hamiltonian explicitly time-dependent, and I had to make some small fixes to the code to take this into account. I've pushed out some new releases (QuantumControl 0.3.1) with the required fixes.

With that, it's now possible to use the "shaped control". I've implemented a proof-of-concept at https://github.com/goerz-testing/2022-09-GRAPE-jl-shaped-controls and specifically in the shaped_control notebook. The implementation should make its way into the package as an official feature at some point, but for now I'll just let this gestate. I'll want to reflect a little on how this might interact with our current plans for non-piecewise-constant propagation and control methods. In the meantime, feel free to play around with this and let me know what you think.

I'll try to have a go at the running costs next weekend.

@dkweiss31
Copy link
Author

Thanks so much for this! I will take a look and let you know my thoughts.

@goerz
Copy link
Member

goerz commented Sep 27, 2022

I have a tentative plan for running costs: JuliaQuantumControl/GRAPE.jl#30

Let me know what you think about the proposed interface (also @seba-car, @alastair-marshall, @Basilewitsch)

@seba-car
Copy link
Member

The proposed interface looks good. It is minimal and consistent with everything.

@goerz
Copy link
Member

goerz commented Sep 27, 2022

Let's have the specific discussion about the running costs over at JuliaQuantumControl/GRAPE.jl#30 ...

@dkweiss31
Copy link
Author

The ShapedControl object seems to be working nicely and as intended (wow does Julia not make it easy to play with non-published packages, thank you for the explainer on your testing branch I would have been lost otherwise). One thing I've noticed is it is a fair bit slower than the original GRAPE optimizer. Have you noticed something similar?

@goerz
Copy link
Member

goerz commented Sep 28, 2022

wow does Julia not make it easy to play with non-published packages

That's a major pain point, indeed. As long as it's just one non-published package, and nothing depends on that package, Pkg.develop gets you pretty far. Once you have dependencies on unpublished packages, the only way to remain sane is to use a local registry.

BTW, especially for any sort of technical problem that doesn't warrant a GH issue: I'm generally also reachable on the #quantumcontrol channel on the Julia Slack (or via private message there)

One thing I've noticed is it is a fair bit slower than the original GRAPE optimizer. Have you noticed something similar?

Not very much, no. I'm seeing about 2.0 seconds per gradient evaluation for the shaped control vs 1.8 seconds for the original example, on one particular workstation. The one obvious possible cause for this would be the # TODO note I put in the code for QuantumPropagators.Controls.getcontrolderiv(generator::Hamiltonian{<:Any,ShapedControlAmplitude}, control) in the notebook: I'm multiplying the control Hamiltonian there with S(t). So that ends up as (ϵₗ(t) (S(t) Ĥₗ)) |Ψ⟩ instead of the more efficient ((ϵₗ(t) S(t)) Ĥₗ) |Ψ⟩ that one would get from leaving the product unevaluated.

@goerz
Copy link
Member

goerz commented Oct 3, 2022

The QuantumControl v0.4.0 release now contains support for running costs over the control fields in GRAPE.

I'd still consider this a bit experimental, so there isn't much explicit documentation around it beyond just the API docstrings. I did try to play around with it a little bit in https://nbviewer.org/gist/goerz/19785600cd688a60dd7e69935ede324a and while I don't see any indication that there's something wrong with the implementation, adding a penalty on the fluence does seem to introduce local traps in the optimization landscape. So I'm not getting very useful results in that example.

I'm not 100% settled on how the λₐ parameter should be set, and whether there's some better default value than 1.0, or some other way to "normalize" running costs. There's also the question of how to report the convergence data (the table that gets printed during the optimization) in the most helpful way.

@dkweiss31 Feel free to play around with this and suggest any possible improvements to the implementation.

Also, in the longer term, if you think the example you're currently working on could be instructive, maybe you can contribute a notebook for the documentation at some point in the future.

I'll probably not be very much available for QuantumControl.jl development during the next couple of weeks, but maybe after that, I'll tackle the L-BFGS-B amplitude bounds.

@dkweiss31
Copy link
Author

Oh great! I will definitely give the running costs a look. And agreed re the slowdown for ShapedControl: I was comparing apples and oranges, comparing the same code it is only marginally slower.

That could be a nice idea re the example notebook: right now my implementation for my problem leans heavily on your example notebooks, but hopefully as I get more comfortable and have a sufficiently different problem from what you already have I can contribute something.

goerz added a commit to JuliaQuantumControl/GRAPE.jl that referenced this issue Dec 1, 2022
the `optimize_grape` routine can now be called with an `upper_bound` and
`lower_bound` parameter. For time-dependent bounds, or different bounds
on different pulses, it is also possible to give `pulse_options` with
keys `upper_bounds` and `lower_bounds`.

This is only implemented for the L-BFGS-B backend. I couln't really
figure out how to add bounds to Optim.jl.

JuliaQuantumControl/QuantumControl.jl#15
goerz added a commit to JuliaQuantumControl/GRAPE.jl that referenced this issue Dec 1, 2022
the `optimize_grape` routine can now be called with an `upper_bound` and
`lower_bound` parameter. For time-dependent bounds, or different bounds
on different pulses, it is also possible to give `pulse_options` with
keys `upper_bounds` and `lower_bounds`.

This is only implemented for the L-BFGS-B backend. I couln't really
figure out how to add bounds to Optim.jl.

JuliaQuantumControl/QuantumControl.jl#15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants