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

Refactor indexing #230

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
Draft

Refactor indexing #230

wants to merge 16 commits into from

Conversation

david-pl
Copy link
Member

@david-pl david-pl commented Dec 17, 2024

This lays the foundation for refactoring the entire indexing implementation. The basic idea is one we already discussed in the very beginning. Essentially, we want to use the following commutation relation for indexed bosonic operators

$$ a_i a_j^\dagger = a_j^\dagger a_i + \delta_{ij} $$

Similarly, for transition operators, we have

$$ \sigma_r^{ij} \sigma_s^{k\ell} = \delta_{jk}\delta_{rs}\sigma_r^{i\ell} + (1 - \delta_{rs})\sigma_r^{ij}\sigma_s^{k\ell} $$

The problem here is the fact that the original product of transition operators on the left-hand side appears again on the right-hand side. This can potentially lead to infinite recursion. Currently, the solution to this problem is to store the fact that in the latter product $r \neq s$ on any sum expression that may occur. This has other problems, mostly the complex implementation and handling all kinds of special cases.

Instead, here I propose to implement the commutation relations using the above relations directly. To prevent infinite recursion on the transition operators, we store an additional field on indexed operators called merge_events, which is just a Vector{UUID}. Whenever two indexed transitions get rewritten according to the above, we generate a random UUID, currently using UUIDs.uuid4(), and store that on two copies of the original transition operators. Therefore, the operators behave just as they should when multiplied together with other operators, except when we find a shared UUID in the merge_events. That can only occur when a product of indexed transitions was returned from the * implementation and thus they have already been merged.
To ensure that in products involving three or more indexed transitions, we also sort by the length of the merge_events vector.

Since this needs to be a full rewrite, I'm opening this as a draft until we add back all the features. If we don't complete the rewrite within this PR, it at least serves as documentation of the fundamental concept, which seems to work.

Feature list:

  • Basic indexed operators and commutation relations
  • Summation
  • Conversion to MTK and numeric solution
  • Scaling (replacing sums by appropriate factors)
  • Correlation functions and spectra together with indexing
  • Measurement back action with indexing

FYI, @ChristophHotter and @j-moser

@david-pl david-pl changed the title Start refactoring indexing; basics seem to work Refactor indexing Dec 17, 2024
@david-pl
Copy link
Member Author

Little update on the status here: derivation of equations, averaging and the cumulant expansion all work so far. I haven't checked the results in detail, but the equations look okay. I'll check that by comparing to a "brute-force" approach later, when I can actually get a numerical solution out. The next step would be to implement complete.

A couple of things I've learned so far:

  • The basic idea here, i.e. implementing commutation relations for indexed operators directly via multiplication (not bothering with a concept of "not equal indices" on sums etc.) seems to work. It makes the code way shorter and easier to read. It's still messy right now (leftover TODOs and a bunch of old commented out code), but it should be easy to clean that up.
  • Indices are always contained in SymbolicUtils.arguments(ex) now, except for IndexedOperators. This makes finding and swapping indices so much easier.
  • Indices are now c-numbers, so you can do some math on them. This is useful, because it allows to express $\delta_{ij}$ simply as i == j. Similarly, instead of $1 - \delta_{ij}$ you can just write i != j (realizing I could do that took me way longer than I'd like to admit).
  • We still need a nice usable way to construct a product of transition operators such as $\sigma_i^{eg}\sigma_j^{ge}$ where $i \neq j$. Right now that's not easily possible, but you may want that e.g. when adding such a product to the list of operators you want to derive an equation for. Otherwise, you always end up with an addition of the two different cases where $i = j$ and where $i \neq j$.
  • I'm still not entirely certain how to deal with "scaling". But I think it should be possible to do this by just substituting the index on all occurring sums. For example, if I turn $\Sigma_i g_i \sigma_i$ into $\Sigma_j g_i\sigma_i$, then the latter is just $\Sigma_j g_i\sigma_i = N g_i \sigma_i$. The nice thing about this would be that this would just be naturally implemented as there's a short-hand when creating a sum that checks whether the summation index actually occurs in the expression you're summing over. If not, then you just return the range of the index times the expression, so N * x.

Some other notes, that can be addressed in a follow-up PR:

  • Why does an Index need a Hilbert space? It's just a number, I don't understand and I think removing it has little effect. I essentially ignored the index Hilbert space throughout the implementation.
  • I'm also not sure whether defining the range of an index should be done on the index itself. Usually, you define that on the object. At the very least, we should have an upper and lower bound on an index. Right now, you always assume it starts at 1 and there's no real reason for that.
  • I'm not sure how far the concept of "doing math on indices" works (not far probably), but it would be interesting to see. It would be nice to e.g. be able to write something such as $\Sigma_i \sigma_i \sigma_{i+1}$ and have it just work.

@ChristophHotter anything you'd like to add here?

@ChristophHotter
Copy link
Member

That sounds very good, nice!
I have a few comments:

  1. Regarding the product of transition operators: If the product (i≠j)*(i=j) = 0 is defined one could maybe write (i≠j) s_i*s_j
  2. I think the scaling idea is very nice. In order to account for the correct terms (e.g. N-1) a possible solution would probably be to set all indices on the LHS of the eqs to a specific value (e.g. j=1) but also the corresponding ones on the RHS. Then substitute the indices of the sums. At the end substitute the remaining indices (on the RHS).
  3. For the function complete() the number of operators that need to be derived can be reduced if the equations get scaled afterward since there are more "redundant" terms - but I guess this is not so important at the moment.
  4. I agree that the index probably does not need a HilbertSpace and the range should be defined later.

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

Successfully merging this pull request may close these issues.

2 participants