Menu

Naming the components of equations

Why this blog post

I have just finished Ticket [#124], which introduced some renaming of very basic classes. And while asking Burkhard for his opinion, he was not able to dig through my explanations. Plus, the Matlab version started a new blog, so I thought I would try to wrap up all the ideas why the new names are great in a blog post and explain along the way how a good part of the C++ version works.

The problem

Let us look at four different equations for propagating some quantum-mechanical state:

The equations are:

  1. An ordinary real-time Schrödinger equation with some Hamiltonian that describes the time-evolution of a wave function.
  2. An ordinary Liouville-von-Neumann (LvN) equation with the same Hamiltonian that describes the time evolution of a density operator.
  3. The Schrödinger equation in imaginary time, again with the same Hamiltonian. This equation has some special uses. In particular, it can be used to relax a wave function to the Hamiltonian's ground state (whose norm decays the slowest).
  4. A LvN equation in imaginary time. This equation is pretty useful in thermodynamics: If you use the unit operator at t=0, the solution of this equation is exp(-Ht), which is exactly the form of the density operator in thermal equilibrium.

The goal with the C++ library was to be able to solve all of these equations with as much reuse as possible. To get there, we first need to decompose the equations into components that we can then combine to suit all our needs. If we look hard at these equations, we notice some common patterns:

  • The left-hand side of the equations can look differently. In practice, we only need two specific cases, namely the real-time propagation and the imaginary-time propagation.
    We encode this information implicitly in the choice of the propagation scheme. The reason is that real- and imaginary-time evolution behaves differently. For example, you can propagate a wave function back and forth in real time, but in imaginary time, the backwards propagation is numerically unstable with exponentially growing errors. So you choose the left-hand side of the equation by picking the solver for the differential equation.
  • Obviously, we have an operator that appears in various ways in the equations. This is a component that should be available directly in the code.
    An operator must be applicable to a wave function or to the ket or bra side of a density operator.
  • The operator itself is not enough. Note that the different equations use the operator in different ways. This information must be encoded somehow as well.
    Hence, we need a separate component that takes one or more operators and turns them into one of the standard equations of motion.
  • Finally, we also want to build systems of equations. The background is that various treatments for open quantum systems use a form where you propagate a density operator in time plus additional auxiliary or "ghost" densities that are coupled to the "real" equation and contain the memory of the environment. Examples are the procedure by David Tannor et al. https://doi.org/10.1063/1.479669 or the hierarchical equations of motion.
    As a simple goal, we can attempt to propagate equations (1) and (2) together. While this is makes no sense, it shows that we could also propagate coupled equations of motion.

This decomposition into four distinct entities is used by the C++ Wavepacket package. Now let us demonstrate how to build the skeleton of a propagation scheme out of that. Let us assume we have a one-dimensional grid set up in a Representation instance called "rep" and let us further assume that the Hamiltonian is just the kinetic energy (i.e., we have a free particle with mass "m"). Then we would set up the equations as follows:

:::c++
auto H = std::make_shared<CartesianKineticEnergy>(rep, 0, mass); // 0 - first degree of freedom

auto equation1 = std::make_shared<SchroedingerEquation>(H);
auto solver1 = std::make_shared<ChebychevPrimitive>(equation1);

auto equation2 = std::make_shared<CommutatorLiouvillian>(H);
auto solver2 = std::make_shared<ChebychevPrimitive>(equation2);

auto equation3 = std::make_shared<SchroedingerEquation>(H);
auto solver3 = std::make_shared<ChebychevRelax>(equation3);

auto equation4 = std::make_shared<OneSidedLiouvillian>(H);
auto solver4 = std::make_shared<ChebychevRelax>(equation4);

auto combinedEquations = std::make_shared<EquationSystem>(2);
combinedEquations->setEntry(0, 0, equation1);
combinedEquations->setEntry(1, 1, equation2);
auto combinedSolver = std::make_shared<ChebychevPrimitive>(combinedEquations);
    // note: this will actually not work, because the Chebychev solver needs the spectrum
    // and refuses to guess it here, but that is a technical detail that I leave out here.

Of course, to get the full simulation going, you need some more boilerplate code, but that is the gist of the setup of the equations. Hence with just changing two lines of code you can turn each of the equations of motion into another one. You can also add operators and Liouvillians to form more complex expressions, but that is beyond the scope of this post.

And what does this have to do with renaming?

Now as an addendum, we can come back to the original reason for this post: I wanted to rename some classes. So far we have established four different components to specify the equations of motion, but how do we name them? Here, naming means both the name of the base class from which the concrete implementation classes derive and the name to use in the documentation for coherence.

The new naming scheme is as follows: The solvers are called PropagationPrimitive (come to think of it, "Solver" might be a better name...anyway), the operators are called "Operator", the Schrödinger equation and Liouvillians are called "Expression", and the system of equations is called "EquationSystem".

The old naming scheme used "OperatorPrimitive" to denote an operator and "LinearOperator" for the equation of motion. The latter was also sometimes called "complex operator" for lack of a better word. To top it off, the system of equations was called "OperatorMatrix". It was pretty confusing and ugly to name two different concepts with almost the same word, so this should be much better now.

Related

Tickets: #124

Posted by Ulf Lorenz 2019-03-08
Attachments:

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.