could you clarify more the following

'complex numbers handled lazily: no efficiency loss if real'

Why should we loose efficiency while using complex numbers? and how much do we loose?

0 votes

Hi, so to work with complex numbers C++ has to store two real numbers (double precision floating point) to represent one complex number. This means twice the memory usage, which is wasteful if one is doing a calculation where the imaginary parts are always zero (such as DMRG with a real-valued Hamiltonian).

In terms of time costs, contracting complex tensors is more costly than contracting real tensors. To illustrate why, if you think about multiplying two real matrices of size NxN, the complexity is N^3. If you then think about representing a complex matrix as a real matrix of size 2N, then the complexity for multiplying the complex matrices will be 8N^3, so a factor of 8 slower. Of course in an optimized BLAS for complex matrices, there are some extra efficiencies, but in general there's always some extra cost for handling complex numbers.

So the strategy in ITensor is to avoid dealing with complex numbers at all whenever possible. This is done by having separate storage types for real and complex tensors. The nice thing is that these storage types can be mixed together e.g. one could make an array of ITensors with some having real and some having complex storage. When contracting two real ITensors you get maximum efficiency. When the contraction involves a complex tensor, there is an extra cost for dealing with complex numbers.

- Miles

Thanks Miles for your quick respond,

You are right, it's all about keeping two numbers instead of one (for complex numbers). But, in some cases, you must use complex numbers when the Hamiltonian is complex (e.g., for the case of complex terms in Hamiltonian). I expect Itensor to handle it as does for real number (obviously you loose efficiency, which is logical ). However, I did not understand; does Itensor understand complex number or we have to manually handle it as you suggested by separating tensors?

Is it possible to define a complex iTensor or not?

You are right, it's all about keeping two numbers instead of one (for complex numbers). But, in some cases, you must use complex numbers when the Hamiltonian is complex (e.g., for the case of complex terms in Hamiltonian). I expect Itensor to handle it as does for real number (obviously you loose efficiency, which is logical ). However, I did not understand; does Itensor understand complex number or we have to manually handle it as you suggested by separating tensors?

Is it possible to define a complex iTensor or not?

Hi, yes there are definitely cases when you must use complex numbers, such as when the Hamiltonian is complex. ITensor does fully understand complex numbers. For example, you can multiply an ITensor by a complex number or put complex couplings into the definition of a Hamiltonian using AutoMPO. Basically anywhere you can use a real number you can also use a complex number with ITensor.

The comment about "no efficiency loss if real" and "complex numbers handled lazily" just means that you don't pay an efficiency penalty for using real numbers, yet the code automatically works for complex numbers (in which case you do pay an unavoidable size and speed penalty).

One convenient thing is that ITensor defines a notation for you that will become standard in later versions of C++. It is that you can write a complex number as a+b_i where a and b are real numbers. So like 1+2_i

The comment about "no efficiency loss if real" and "complex numbers handled lazily" just means that you don't pay an efficiency penalty for using real numbers, yet the code automatically works for complex numbers (in which case you do pay an unavoidable size and speed penalty).

One convenient thing is that ITensor defines a notation for you that will become standard in later versions of C++. It is that you can write a complex number as a+b_i where a and b are real numbers. So like 1+2_i

...