-
Notifications
You must be signed in to change notification settings - Fork 124
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
[ITensorGaussianMPS] Add time-dependent Green functions #884
Conversation
Benchmark resultJudge resultBenchmark Report for /home/runner/work/ITensors.jl/ITensors.jlJob Properties
ResultsA ratio greater than
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfoTarget
Baseline
Target resultBenchmark Report for /home/runner/work/ITensors.jl/ITensors.jlJob Properties
ResultsBelow is a table of this job's results, obtained by running the benchmarks.
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfo
Baseline resultBenchmark Report for /home/runner/work/ITensors.jl/ITensors.jlJob Properties
ResultsBelow is a table of this job's results, obtained by running the benchmarks.
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfo
Runtime information
|
Codecov Report
@@ Coverage Diff @@
## main #884 +/- ##
=======================================
Coverage 75.77% 75.77%
=======================================
Files 76 76
Lines 8011 8011
=======================================
Hits 6070 6070
Misses 1941 1941 Continue to review full report at Codecov.
|
Benchmark resultJudge resultBenchmark Report for /home/runner/work/ITensors.jl/ITensors.jlJob Properties
ResultsA ratio greater than
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfoTarget
Baseline
Target resultBenchmark Report for /home/runner/work/ITensors.jl/ITensors.jlJob Properties
ResultsBelow is a table of this job's results, obtained by running the benchmarks.
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfo
Baseline resultBenchmark Report for /home/runner/work/ITensors.jl/ITensors.jlJob Properties
ResultsBelow is a table of this job's results, obtained by running the benchmarks.
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfo
Runtime information
|
Nice and simple, thanks! Just to clarify, is I've been trying to think of a good name for that matrix and stick to a convention. In ITensorGaussianMPS I've been referring to that matrix as a "Slater determinant" but that doesn't seem quite right, since in actuality the Slater determinant is the determinant of that matrix. Anyway, kind of an aside, but I wanted to pick your brain about that. Maybe we could be more explicit with the names, like: G_R -> retarded_greens_function
G_G -> greater_greens_function
G_L -> lesser_greens_function I think I would forget what |
Yes, I was talking with Angkun about the name of Sure thing about changing the function names. More descriptive names are good. You raise an interesting point about the number of particles I was meaning to mention. The way I wrote the code, you can specify the number of particles (I called it Lastly, something I'm still wrapping my head around is that the formula for GR doesn't depend on the number of particles (however that is determined). I see how it comes through in the derivation, but it's just counterintuitive when you think of how its computed say from an MPS. The greater and lesser Green functions definitely do depend on the number of particles. (Better names for them, but which aren't standard, would be particle and hole Green functions.) |
Benchmark resultJudge resultBenchmark Report for /home/runner/work/ITensors.jl/ITensors.jlJob Properties
ResultsA ratio greater than
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfoTarget
Baseline
Target resultBenchmark Report for /home/runner/work/ITensors.jl/ITensors.jlJob Properties
ResultsBelow is a table of this job's results, obtained by running the benchmarks.
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfo
Baseline resultBenchmark Report for /home/runner/work/ITensors.jl/ITensors.jlJob Properties
ResultsBelow is a table of this job's results, obtained by running the benchmarks.
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfo
Runtime information
|
I think my main issue with using "orbitals" or "natural orbitals" is not just that it is chemistry oriented, but that it seems to imply something about where they came from (like they were the result of a Hartree-Fock calculation, are somehow related to atoms or molecules, etc. whereas they could just be any set of single-particle states). I guess "orbital" (as opposed to "natural orbital") is a more general term but it still feels very loaded to me. However, the only alternative I can think of is something like "single particle states", which is a mouthful. So for example the options I can think of are: slater_determinant_to_mps(s, Φ; blocksize=4) # Current interface
orbitals_to_mps(s, Φ; blocksize=4)
orbital_matrix_to_mps(s, Φ; blocksize=4)
single_particle_states_to_mps(s, Φ; blocksize=4) Even though Also, what about:
since they are called "Green's functions" (I changed my post above to reflect that). I was going to suggest I had also gone back and forth about passing around:
However, you don't always have the Hamiltonian, so you wouldn't always know the energies. Also, there may be cases where the single-particle states aren't ordered by occupation (for example a GMPS, where they generally alternate between occupied and unoccupied). So maybe it needs to be handled by context. Really the most general format is option 3., an struct SingleParticleStates{T}
states::Matrix{T}
occupations::Vector{T}
end Before I was putting things together piece by piece, but I think it's good to hash out these design choices as we start adding more functionality. One thing I definitely like is being able to use |
Good points. My thought about the term "orbitals" is that while it does originate from chemistry, I think it's an example where that field has come up with a nice term, while condensed matter has not. (E.g. "single-particle eigenstates" is too long.) We could call the columns of Φ "states" perhaps. A small point is that I'm fairly sure the natural orbitals are not tied to Hartree-Fock per se. Rather, they are the orbitals that diagonalize the correlation matrix <cdag_i c_j>. So the connection to Hartree-Fock is only if one gets a Slater det that way, and then one can talk about that Slater det's natural orbitals. In short, I personally like "orbitals" and think people would get used to it. But I'm ok with "single-particle states" as a backup since it would definitely be clear to everyone what is meant. Maybe there's a third option. Yes to something like The question about what to pass around is kind of tricky. I would advocate being as flexible about it as makes sense. One point is that sometimes we know the energies have to be passed: this is the case for any time-dependent quantity. Also the unoccupied states are sometimes essential, such as for the retarded and greater Green functions. Left to my own devices, I probably would have done this:
One thing I like about the above is that the user could always keep all the orbitals around if they wish, and not have to mess with slicing columns, and can control things automatically through the energy or manually with Your idea of a struct with occupations is really good though because it makes it explicit to the user that they are making a choice, versus having some implicit thing. Also it allows the occupancies to be fractional which could become important! The most minor point for me is the Green's versus Green function naming question. More and more I like calling them Green functions (similar to how we say Bessel functions, not Bessel's functions) and I think the 's comes just from the fact that Green sounds like an adjective by itself. But I do think Green's is the more common usage that I hear. |
Agreed that "orbitals" and "natural orbitals" have more general definitions, my main gripe with them is that they seem to be very closely tied to algorithms like Hartree-Fock whenever they are presented, and in those contexts are often not well defined (I really wish chemists would just use second quantization and define things more carefully...). Anyway, griping aside, let's go with "orbital" as the terminology but just make sure to define it well, and in the docs say things like "the input is a matrix of orbitals (single particle states) [...]". Agreed with your general proposal for having flexible inputs (i.e. allow people to pass the minimal information needed). If we go with the "orbitals" terminology, then the type could be: struct Orbitals{T,R}
states::Matrix{T}
occupations::Vector{R}
end This struct could act as a common internal data structure, and an optional external interface. For example we might have a function that can accept a variety of inputs as you propose (orbitals and a Hamiltonian, orbitals and number of particles, just occupied orbitals, an Similarly, I have been considering defining a type: struct CorrelationMatrix{T}
correlation_matrix::Matrix{T}
end which again would be mostly for internal use to help with code organization, so we could define things like In general I prefer not to have a proliferation of types and/or expose users to too many different types, but in this case there are many objects which are all matrices that can be converted to each other in one way or another so I think types can be used to help distinguish them and avoid a proliferation of function names. As for |
Benchmark resultJudge resultBenchmark Report for /home/runner/work/ITensors.jl/ITensors.jlJob Properties
ResultsA ratio greater than
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfoTarget
Baseline
Target resultBenchmark Report for /home/runner/work/ITensors.jl/ITensors.jlJob Properties
ResultsBelow is a table of this job's results, obtained by running the benchmarks.
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfo
Baseline resultBenchmark Report for /home/runner/work/ITensors.jl/ITensors.jlJob Properties
ResultsBelow is a table of this job's results, obtained by running the benchmarks.
Benchmark Group ListHere's a list of all the benchmark groups executed by this job:
Julia versioninfo
Runtime information
|
These types are a good idea here because I've had trouble too with different matrices floating around in non-interacting calculations and getting worried that I'm mixing them up, or mixing up the meaning of rows and columns etc. The Orbitals type here, bundling the occupations, is especially nice because it lets some of those automatic features happen while being explicit about it. But even better it allows things like using only the occupied orbitals while still keeping the unoccupied ones around, which are necessary for certain dynamical properties. Lastly, it lets us have fractional occupations which is neat. I also just took a look at the Fermi.jl and PySCF documentation. Fermi uses the term orbitals in the context of Hartree-Fock and doesn't mention the term elsewhere, but also has very minimal docs. PySCF does seem to use the term orbitals very liberally, referring also to "correlated orbitals" and "localized orbitals" in the context of various algorithms like coupled cluster and so on. So I think it's a pretty safe term to use in a general sense. I'm totally ok with |
After discussing with Miles, the conclusion was to define a type struct SlaterDeterminant{S,T}
orbitals::Matrix{S}
occupations::Vector{T}
end The occupied and unoccupied orbitals (single-particle basis states) of the Slater determinant (free fermion state) are stored as columns of the square Then, the interface for making an MPS from a Slater determinant would change from: N = 20
Nf = N÷2
t = 1.0
h = Hermitian(diagm(1 => fill(-t, N-1), -1 => fill(-t, N-1)))
_, u = eigen(h)
Φ = u[:, 1:Nf]
ψ0 = slater_determinant_to_mps(s, Φ; blocksize = 4) to: MPS(SlaterDeterminant(Φ), s; blocksize = 4) The SlaterDeterminant(Φ::Matrix) # (N, Nf) = size(Φ), fills in the unoccupied orbitals with a full QR decomposition
SlaterDeterminant(Φ::Matrix, Nf::Int) # (N, N) = size(Φ)
SlaterDeterminant(Φ::Matrix, occupations::Vector{Int}) # Nf = count(isone, occupations) |
I'm trying to clear out old PRs and issues. ITensorGaussianMPS.jl is now in the repository https://github.com/ITensor/ITensorGaussianMPS.jl so this should get moved over there once we are ready, I'll close this for now but we can keep the branch as a reference. |
Not a problem. In hindsight, since these functions don't do anything specific to MPS it might even be better for them to live in a separate package. They can be super useful for e.g. benchmarking time-dependent codes though and have nice clean formulas. So it would be a good thing to come back to sometime. |
Yes, that's a good point. Ideally there would be a separate Gaussian state package that is independent of ITensor entirely, which could use OpSum as a nice interface for symbolic representions of quadratic Hamiltonians. Splitting off ITensorMPS and eventually ITensors.LazyApply and ITensors.Ops all help move things in that direction. |
This PR adds three functions, G_R, G_G, and G_L, which compute the retarded, greater, and lesser Green functions as a function of time. By default they do the following:
but both of these defaults are overridable by keyword arguments. The
sites
keyword argument works similarly to the same keyword in theexpect
andcorrelation_matrix
functions in ITensor. One can pass a keyword argumentNpart
to set the number of particles (occupied orbitals) manually.I'm happy to change the names of any of these (functions or keyword arguments).
Also we could add the advanced Green function but I'm not sure if anyone uses it.