val() function and state() function in the "Electron" site type source code

+1 vote

Hi,

I have been trying to define my own SiteType by following the "Electron" site type source code on Github, but I got confused by the different versions where the usage of val() function and state() function varies.

To be precise, for https://github.com/ITensor/ITensors.jl/blob/main/src/physics/site_types/electron.jl
The val() function is used, and the state() function is assigned vectors.
However, for https://github.com/ITensor/ITensors.jl/blob/629_setindex_complex/src/physics/site_types/electron.jl
The val() function is not used, and the state() function is assigned integer scalars.

I roughly understand that the state() function serves as a basis for defining the matrix operators, so are both vectors and scalars fine?
Also, what exactly does the val() function do?

Thanks a lot for your help,
Mason

+1 vote

Hi Mason,
Thanks for asking about this. One potential source of confusion is that we are changing the behavior of these functions between the 0.1.x versions of ITensors.jl (the current release being 0.1.41 right now) and the upcoming 0.2.0 version of ITensors.jl, which corresponds to the current "main" branch.

The first link you provided is to the 'main' branch whereas the second link is to an unmerged PR branch that is forked off of the 0.1.41 version. So the code there is different, and the function state has a different meaning.

Here is a summary of the situation in the current 0.1.x versions versus the upcoming 0.2.0 version:

• In the 0.1.x versions:

• the function state makes an IndexVal (Index stored together with an integer value) given an input such as "Up" or "Dn". This is implemented in the site_types/ files as returning just the integer, and the Index is included automatically by the calling function (generic implementation of state).
• there is no "val" function of this type (other than an unrelated one for getting part of an IndexVal)
• After the 0.2.0 release (probably in the next week or so):

• the function val replaces the older state function, and just returns an integer value. So for "Up" for a spin it would return 1, and for "Dn" it would return 2 for S=1/2 and 3 for S=1, etc.
• the function state now has a different meaning, and returns a single-site wavefunction (single site state) corresponding to different provided names. So providing "Up" will return a wavefunction with the spin being exactly up, where as providing "X+" will return a single-site wavefunction with the spin in the positive X direction, etc.

Hope that clarifies the situation! Much of the code, like the strings of "states" you pass to the productMPS constructor, will have exactly the same interface as before. But if you have code which directly calls the state function it will have to be upgraded in the new version. We will provide an upgrade guide in the docs.

Best,
Miles

commented by (390 points)
edited by
Thank you Miles, that clarifies a lot.

But somehow the productMPS is still not working and gives me the error "LoadError: ArgumentError: Overload of "state" function not found for Index tags "CFermion,Site,n=1", even though I can print out the states and sites in productMPS(sites, states).

I copy and paste my code below for your reference: (I followed the "Electron" site type closely)

using ITensors

ITensors.space(::SiteType"CFermion") = 4
function ITensors.space(
::SiteType"CFermion";
conserve_qns=false,
conserve_Q1=conserve_qns,
conserve_nfparity=conserve_qns,
qnname_Q1="Q1",
qnname_nfparity="NfParity",
)
if conserve_nfparity && conserve_Q1
return [
QN((qnname_nfparity, 0, -2), (qnname_Q1, 0)) => 1,
QN((qnname_nfparity, 1, -2), (qnname_Q1, +1)) => 1,
QN((qnname_nfparity, 1, -2), (qnname_Q1, +2)) => 1,
QN((qnname_nfparity, 0, -2), (qnname_Q1, +3)) => 1
]
end
return 4
end

#val(::ValName"Emp", ::SiteType"CFermion") = 1
#val(::ValName"f1", ::SiteType"CFermion") = 2
#val(::ValName"f2", ::SiteType"CFermion") = 3
#val(::ValName"f1f2", ::SiteType"CFermion") = 4
ITensors.state(::StateName"Emp", ::SiteType"CFermion") = [1.0,0,0,0]
ITensors.state(::StateName"f1", ::SiteType"CFermion") = [0.0,1,0,0]
ITensors.state(::StateName"f2", ::SiteType"CFermion") = [0.0,0,1,0]
ITensors.state(::StateName"f1f2", ::SiteType"CFermion") = [0.0,0,0,1]

function ITensors.op!(Op::ITensor, ::OpName"C1", ::SiteType"CFermion", s::Index)
Op[s' => 1, s => 2] = 1.0
return Op[s' => 3, s => 4] = 1.0
end

function ITensors.op!(Op::ITensor, ::OpName"C1d", ::SiteType"CFermion", s::Index)
Op[s' => 2, s => 1] = 1.0
return Op[s' => 4, s => 3] = 1.0
end

function ITensors.op!(Op::ITensor, ::OpName"C2", ::SiteType"CFermion", s::Index)
Op[s' => 1, s => 3] = 1.0
return Op[s' => 2, s => 4] = 1.0
end

function ITensors.op!(Op::ITensor, ::OpName"C2d", ::SiteType"CFermion", s::Index)
Op[s' => 3, s => 1] = 1.0
return Op[s' => 4, s => 2] = 1.0
end

function ITensors.op!(Op::ITensor, ::OpName"N1", ::SiteType"CFermion", s::Index)
Op[s' => 2, s => 2] = 1.0
return Op[s' => 4, s => 4] = 1.0
end

function ITensors.op!(Op::ITensor, ::OpName"N2", ::SiteType"CFermion", s::Index)
Op[s' => 3, s => 3] = 1.0
return Op[s' => 4, s => 4] = 1.0
end

has_fermion_string(::OpName"C1", ::SiteType"CFermion") = true
has_fermion_string(::OpName"C1d", ::SiteType"CFermion") = true
has_fermion_string(::OpName"C2", ::SiteType"CFermion") = true
has_fermion_string(::OpName"C2d", ::SiteType"CFermion") = true

let
N = 6

sites = siteinds("CFermion",N; conserve_qns = true)
println(sites)

states = [mod(i,2)==0 ? "f1" : "f2" for i=1:N]
println(states)

psi0 = productMPS(sites,states)
println(psi0)

end
commented by (56.3k points)
Quick question: what version of ITensor are you using? The current tagged version 0.1.41 (what you would obtain by default through the Julia package manager) or the main/master branch ? (Development version?)
commented by (390 points)
edited by
It's v0.1.36. Should I try to update?
Edit: I just updated to v0.1.41, and the same error persists.
commented by (56.3k points)
Thanks. So my answer above may have been overly long. The part of the answer that applies to your setup (because you aren't using the development / main branch version 0.2 which is fine) is that you should define the ITensors.state function to return a single number, not a vector. There is no val function yet in the version of ITensor you're using, or indeed in the latest tagged release of ITensor either.

Once we release the 0.2.0 version then the behavior of state will switch at that time (to what you're seeing on Github, which is the main branch) and also val will be introduced.
commented by (390 points)
I tried using 1,2,3,4 to label my four states, but it gives the same error. Thanks again.
commented by (56.3k points)
I see. So I think I spotted the problem: apparently we also switched the order of the arguments to state in the new version of the code (which you were looking at to compare to). If you look at this version of the code which matches the version you are using (0.1.36) then you will see that you should put the SiteType first then the StateName. Does that now work as expected? (Basically you should be able to closely follow the design of this file linked below:)

https://github.com/ITensor/ITensors.jl/blob/v0.1.36/src/physics/site_types/fermion.jl

[Note how on Github it is showing that this version of the file is for 0.1.36.]
commented by (390 points)
Ah I see, now it works. Thank you so much!
commented by (56.3k points)
commented by (390 points)
Hi Miles, sorry to bother you again. I tried to add in interactions based on the previous code by using (as a minimal example):

ampo += g, "C2dC1", 1, "C1", 2
ampo += g, "C1d", 2, "C1dC2", 1

where the "C2dC1" and  "C1dC2" are defined the usual way:

function ITensors.op!(Op::ITensor, ::OpName"C2dC1", ::SiteType"CFermion", s::Index)
return Op[s' => 3, s => 2] = 1.0
end
function ITensors.op!(Op::ITensor, ::OpName"C1dC2", ::SiteType"CFermion", s::Index)
return Op[s' => 2, s => 3] = 1.0
end

However, making MPO by using MPO(sites, states) gives me the error: " No block found with QN equal to QN(("NfParity",1,-2),("Q1",0))" when I set conserve_qn to be true.

The strange part is the QN(("NfParity",1,-2),("Q1",0)), because it has 0 charge, but fermion parity is 1, which isn't supposed to appear based on my definition of the local Hilbert space and the operators. I'm not sure what I'm missing here.

Thanks again,
-Mason
commented by (56.3k points)
Hi Mason,
So I noticed something unusual about the QN subspaces in your custom site type that might be causing this issue. The thing I noticed is that your "Q1" quantum numbers have the values +0,+1,+2,+3. In contrast, for an Electron site the particle number values would be 0,1,1,2 (corresponding to no electrons, one up electron, one down electron, or both kinds of electrons).

So could you explain more about the choice of 0,1,2,3 and what is behind it? Or do you think that's possibly an error?

Miles
commented by (390 points)
Hi Miles, I think I just found out the problem. The simple test interaction added doesn't preserve Fermion parity, even though my eventual goal is to study parity-conserving interaction. So the error pops up when I was trying to enforce parity.

Sorry that I wasn't clear enough in my question. In my case, f1 is a fermion with charge 1 and f2 is a fermion with charge 2. Both spinless. The QN name is "Q1" because I'll add another "Q2" quantum number in future.