I am currently trying to implement a spin model in ITensors.jl, where I can adjust the spin representation arbitrarily (S = 1/2, 1, 3/2, …, infinity). For this I define the local Hilbert space & its operators manually, depending on the spin dimension. My current solution is really ugly:
define the spin dimension as a global variable
include the .jl file of the spin model, where the local Hilbert space & operators will be defined based on the spin dimension that was set before in the global variable before
use all the ITensors functionality
Is there a more elegant way to do this? In the end, I would like to compute some observable for various spin dimensions, so it would be cool if the local Hilbert space could be redefined from inside a function.
Could you provide more information about what you are trying to do, like a small example? (I don’t mean a code example but like an example of the math you are trying to solve or a brief, step by step algorithm.) Are you wanting to solve a system where the spin S value is fixed, but then next solve a different system with a different local spin S value? Or are you mixing different S’s on different sites and wanting to change them dynamically during the calculation? Thanks –
The actual model I’m implementing is some quantum link model with an additional local gauge constraint, but it’s analogous to a spin model.
I would like to implement exactly the first option that you mentioned, where the local Hilbert space dimension on all sites is the same and fixed to some S value. Then I want to increase S, solve the entire system again, and so on.
Depending on the overall S, I have therefore have to define the local Hilbert space and various local site operators.
To achieve this, would it be possible to define a SiteType that depends on a parameter (the S, or equivalently 2S+1 to have integers)? Such that I could construct the local Hilbert space for general S like:
function ITensors.space(::SiteType("Spin", spin_dim))
# compute the local site Hilbert space dimension based on spin_dim
# ...
return dimension
end
and the operators like:
function ITensors.op(::OpName"Sz", ::SiteType("Spin", spin_dim))
# compute the local site operator based on spin_dim
# ...
return operator
end
Then when using this custom site Hilbert space, it would be cool to be able to write for example:
# ...
inds = siteinds(::SiteType("Spin", 5), N) # some value of S
# ...
# do some DMRG calculation with this S
# ...
inds = siteinds(::SiteType("Spin", 3), N) # different value of S
# ...
# do another DMRG calculation with the new S
# ...
Yes, that is the sort of idea I would recommend trying. The only difference from how to code it versus what you wrote would be that I would recommend passing the spin_dim variable as just a keyword argument, not inside of the SiteType object itself.
To do that, all you have to do is to make your op overload take keyword arguments, with any names that you like. Then when you call op just pass these keyword arguments and use them inside your function.
Here is a small example:
# Make a space for a new SiteType
ITensors.space(::SiteType"S=3/2") = 4
# Define an op overload taking a keyword argument named "factor"
function ITensors.op(::OpName"Custom", ::SiteType"S=3/2"; factor=1.0, kws...)
M = [+3/2 0 0 0
0 +1/2 0 0
0 0 -1/2 0
0 0 0 -3/2]
return M*factor # here we multiply the operator by this factor
end
# Make a site ind of type "S=3/2"
s = siteind("S=3/2")
# Make a "Custom" operator with a factor of 2.0
O = op("Custom",s; factor=2.0)
@show O
I hope that’s clear enough. Please try it out in some small examples and let me know if you run into any issues or have more questions about it.
Thanks for the fast reply! This looks like a good solution, but does OpSum still work in this case, since the operators require an additional argument? In that case, I would need to pass the spin_dim everytime when I use op(...), so that’s why I thought about instead putting the spin_dim in the definition of a custom SiteType.
Maybe this is not a good idea, but I thought about something like this:
struct Spin{spin_dim} end; # used for the custom SiteType
function ITensors.space(::SiteType{Spin{spin_dim}}) where {spin_dim}
# calculate local site Hilbert space dim, based on spin_dim
# ...
return dimension
end
My thought was that it’s then possible to obtain the site indices using:
# for convenience
Spin(spin_dim) = SiteType{Spin{spin_dim}}()
N = 20
inds = siteinds(Spin(4), N) # some choice of spin dimension
# ... perform DMRG ...
inds = siteinds(Spin(5), N) # another choice of spin dimension
# ... perform another run of DMRG ...
This doesn’t throw an error, but the command siteinds(Spin(4), N) just gives back nothing. I looked a bit into the ITensors.jl code for that and I think I’m missing something with the Tag?
Would you know maybe how to make this work, or do you think this approach in general is not good and I should just pass the spin_dim as an additional argument?
Good point about the compatibility with OpSum. While it actually is possible to pass keyword arguments to OpSum, it is more cumbersome so maybe not the best option for your case.
I thought of a better way that I think you’ll like. Here are the steps:
Make your space overload for your custom “Spin” SiteType take a size keyword argument:
ITensors.space(::SiteType"Spin"; size=2) = size
Then, when making operators, use the variant of op that takes an Index. This is an older type of op overload we don’t advertise as much which you can think of as a more advanced version. Since the Index gets passed, you can read off the size as the dimension of the index:
function ITensors.op(::OpName"Custom", ::SiteType"Spin", s::Index)
size = dim(s)
if size == 2
M = [+1/2 0
0 -1/2]
elseif size == 3
M = [+1 0 0
0 0 0
0 0 -1]
elseif size == 4
M = [+3/2 0 0 0
0 +1/2 0 0
0 0 -1/2 0
0 0 0 -3/2]
end
return ITensor(M,s',s)
end
You can use the above code like this:
N = 4
size = 2
# Make siteinds array
s = siteinds("Spin",N; size)
@show s
# Make custom operator
O = op("Custom",s[1])
@show O
And you should be able to use it in OpSum in the normal way, without passing any extra keyword arguments except for the initial call to siteinds.