This discussion is extremely helpful, but let me try to pull apart what we mean by “norm” and “magnitude” in these situations. The docstring of CmmonSolve.Solve
has that equation that (if I read it correctly) says a step is accepted if
\frac{\text{err}}{a + \bar{u} \, r} < 1
where \text{err} is an (absolute) local error estimate (e.g., the comparison to the higher order of the solver, I presume), a is my abstol
, and r is my reltol
. The \bar{u} is described as
\bar{u} = \max(u_{\text{prev}}, u)
I’m not 100% sure what that means. I’m guessing u_{\text{prev}} is just the value from the previous time step, and accounts for if u goes from e.g. 1
to 1e-10
in a time step, 1
is still the relevant scale. But maybe let’s ignore that and read this as \bar{u} = |u|.
Now, I get the impression that this equation is elementwise. That is, u is each component of my complex state vector |\Psi\rangle (or, more precisely, every real and imaginary part of each component). Which would mean that
isn’t quite right for norm
being the standard Euclidian norm of the entire vector |\Psi\rangle. I think it also means that the recommendation to set either reltol
or abstol
, with the other set to 0 might need a little more nuance.
Let’s stick to state vectors |\Psi\rangle from unitary quantum mechanics. That means norm(Ψ)
is always going to be 1, but each real and imaginary part of each component of Ψ
is going to be some number between -1 and 1. It would be fairly common for one component being exactly 1 and the others exactly zero, or for a handful of components to have a magnitude on the order of 10^{-1} or 10^{-2} and the other components on the order of 10^{-14} (floating point noise). I would care about the (relative) error of the larger components, but obviously I wouldn’t care about the floating point noise.
If I’m thinking through this correctly, in this situation, if I set abstol=0
, and reltol=1e-10
, I would demand a relative error of 10 significant digits even on the floating point noise, which seems like it would be much too high a precision and only blow up my runtime. On the other hand, if I set abstol=1e-10
and reltol=0
, I’m not quite nailing down the precision of the larger components that I actually care about: There might just be a single component with value 1.0
, or the state might be a superposition of 50 basis states, so 50 components with amplitudes 1e-2
– and an absolute error of 10^{-2} on 1.0 vs. 0.01 is obviously a different relative error. That would probably still be manageable, so
seems like not a bad recommendation. Still, I think the best approach (which would go straight into the defaults of the ODE backend of QuantumPropagators
) would be to use abstol=1e-14
to deal with the floating point noise (don’t care about anything approaching machine precision), and reltol=1e-8
for the number of significant digits in the larger components that I actually care about. The reltol
might be something that I can vary depending on the use case, with smaller values if I really want to know the solution “to machine precision” or a larger value if I just need an approximate solution. I would generally then also consider that reltol
to be a reasonably good measure (up to an extra digit or two) for the norm(Ψ - Ψ_exact)
at the end of the propagation, while accepting the caveat that
It would be great if someone can confirm that the “step error condition” is in fact element-wise.
still seems like a major foot-gun. Specifically, what I ran into was setting only abstol
, but then the default value of reltol
being the thing that actually dominates the simulation (and the default reltol
being too small for this kind of problem).
very much seems like something that would avoid this footgun. But for now, I think the lesson is to always set both abstol
and reltol
. Either with reltol=0
and some abstol
, or (better), by setting reltol
to the desired relative error and abstol
always to 1e-14
(or, generally, whatever “zero” is for the coefficients of the vector).
P.S.: I think I might rename this topic to “Setting abstol
and reltor
when solving the Schrödinger equation with OrdinaryDiffEq
”, because that’s really what ended up being the crux of the matter.
Also, for those of you with an interest in quantum physics, as a tidbit for context, this is the notebook I was working on when a ran into this problem: a little review of what happens numerically when going from “adiabatic” to “non-adiabatic” in the dynamics of an avoided (Landau-Zener) crossing: https://goerz-research.github.io/2025-02-04_Landau_Zener/LandauZener.html