From 74fa4f8b90d181f06f00083ef35de18675e025fa Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Tue, 7 May 2024 20:30:20 +0000 Subject: [PATCH] build based on c1b9db9 --- previews/PR1410/AdvancedUsageGuide.html | 2 +- previews/PR1410/CodeTiming.html | 2 +- .../ContractionSequenceOptimization.html | 10 +- previews/PR1410/DMRG.html | 2 +- previews/PR1410/DMRGObserver.html | 4 +- previews/PR1410/DeveloperGuide.html | 2 +- previews/PR1410/Einsum.html | 78 +++++------ previews/PR1410/HDF5FileFormats.html | 2 +- previews/PR1410/ITensorType.html | 80 +++++------ previews/PR1410/IncludedSiteTypes.html | 2 +- previews/PR1410/IndexSetType.html | 6 +- previews/PR1410/IndexType.html | 26 ++-- previews/PR1410/MPSandMPO.html | 130 +++++++++--------- previews/PR1410/Multithreading.html | 4 +- previews/PR1410/Observer.html | 2 +- previews/PR1410/OpSum.html | 6 +- previews/PR1410/ProjMPO.html | 6 +- previews/PR1410/ProjMPOSum.html | 6 +- previews/PR1410/QN.html | 4 +- previews/PR1410/QNTricks.html | 90 ++++++------ previews/PR1410/RunningOnGPUs.html | 2 +- previews/PR1410/SiteType.html | 28 ++-- previews/PR1410/Sweeps.html | 6 +- previews/PR1410/UpgradeGuide_0.1_to_0.2.html | 2 +- previews/PR1410/examples/DMRG.html | 2 +- previews/PR1410/examples/ITensor.html | 18 +-- previews/PR1410/examples/MPSandMPO.html | 2 +- previews/PR1410/examples/Physics.html | 6 +- previews/PR1410/faq/DMRG.html | 2 +- previews/PR1410/faq/Development.html | 2 +- previews/PR1410/faq/HPC.html | 2 +- previews/PR1410/faq/JuliaAndCpp.html | 2 +- previews/PR1410/faq/JuliaPkg.html | 2 +- previews/PR1410/faq/QN.html | 2 +- .../faq/RelationshipToOtherLibraries.html | 2 +- .../PR1410/getting_started/DebugChecks.html | 2 +- .../PR1410/getting_started/Installing.html | 2 +- .../PR1410/getting_started/NextSteps.html | 2 +- .../PR1410/getting_started/RunningCodes.html | 2 +- previews/PR1410/index.html | 2 +- previews/PR1410/search.html | 2 +- previews/PR1410/search_index.js | 2 +- previews/PR1410/tutorials/DMRG.html | 4 +- .../PR1410/tutorials/MPSTimeEvolution.html | 2 +- previews/PR1410/tutorials/QN_DMRG.html | 2 +- 45 files changed, 283 insertions(+), 283 deletions(-) diff --git a/previews/PR1410/AdvancedUsageGuide.html b/previews/PR1410/AdvancedUsageGuide.html index d7f3fa9464..1253c4e14d 100644 --- a/previews/PR1410/AdvancedUsageGuide.html +++ b/previews/PR1410/AdvancedUsageGuide.html @@ -471,4 +471,4 @@ myscale2! (generic function with 1 method) julia> @btime myscale2!(A, 2) setup = (A = randomITensor(i)); - 3.549 μs (2 allocations: 112 bytes)

A very efficient function is written for the Tensor type. Then, the ITensor version just wraps the Tensor function by calling it after converting the ITensor to a Tensor (without any copying) with the tensor function. This is the basis for the design of all performance critical ITensors.jl functions.

+ 3.549 μs (2 allocations: 112 bytes)

A very efficient function is written for the Tensor type. Then, the ITensor version just wraps the Tensor function by calling it after converting the ITensor to a Tensor (without any copying) with the tensor function. This is the basis for the design of all performance critical ITensors.jl functions.

diff --git a/previews/PR1410/CodeTiming.html b/previews/PR1410/CodeTiming.html index 91558b1625..605b3d1000 100644 --- a/previews/PR1410/CodeTiming.html +++ b/previews/PR1410/CodeTiming.html @@ -1,2 +1,2 @@ -Timing and profiling · ITensors.jl

Timing and Profiling your code

It is very important to time and profile your code to make sure your code is running as fast as possible. Here are some tips on timing and profiling your code.

If you are concerned about the performance of your code, a good place to start is Julia's performance tips.

Timing and benchmarking

Julia has many nice timing tools available. Tools like @time and TimerOutputs can be used to measure the time of specific lines of code. For microbenchmarking, we recommend the BenchmarkTools package. For profiling your code, see the Julia documentation on profiling.

+Timing and profiling · ITensors.jl

Timing and Profiling your code

It is very important to time and profile your code to make sure your code is running as fast as possible. Here are some tips on timing and profiling your code.

If you are concerned about the performance of your code, a good place to start is Julia's performance tips.

Timing and benchmarking

Julia has many nice timing tools available. Tools like @time and TimerOutputs can be used to measure the time of specific lines of code. For microbenchmarking, we recommend the BenchmarkTools package. For profiling your code, see the Julia documentation on profiling.

diff --git a/previews/PR1410/ContractionSequenceOptimization.html b/previews/PR1410/ContractionSequenceOptimization.html index 438396d773..563652d29e 100644 --- a/previews/PR1410/ContractionSequenceOptimization.html +++ b/previews/PR1410/ContractionSequenceOptimization.html @@ -1,15 +1,15 @@ -Contraction sequence optimization · ITensors.jl

Contraction sequence optimization

When contracting a tensor network, the sequence of contraction makes a big difference in the computational cost. However, the complexity of determining the optimal sequence grows exponentially with the number of tensors, but there are many heuristic algorithms available for computing optimal sequences for small networks[1][2][3][4][5][6]. ITensors.jl provides some functionality for helping you find the optimal contraction sequence for small tensor network, as we will show below.

The algorithm in ITensors.jl currently uses a modified version of[1] with simplifications for outer product contractions similar to those used in TensorOperations.jl.

Functions

ITensors.ContractionSequenceOptimization.contraction_costFunction
contraction_cost(A; sequence)

Return the cost of contracting the collection of ITensors according to the specified sequence, where the cost is measured in the number of floating point operations that would need to be performed to contract dense tensors of the dimensions specified by the indices of the tensors (so for now, sparsity is ignored in computing the costs). Pairwise costs are returned in a vector (contracting N tensors requires N-1 pairwise contractions). You can use sum(contraction_cost(A; sequence)) to get the total cost of the contraction.

If no sequence is specified, left associative contraction is used, in other words the sequence is equivalent to [[[[1, 2], 3], 4], …].

source
NDTensors.contractFunction
contract(ψ::MPS, A::MPO; kwargs...) -> MPS
+Contraction sequence optimization · ITensors.jl

Contraction sequence optimization

When contracting a tensor network, the sequence of contraction makes a big difference in the computational cost. However, the complexity of determining the optimal sequence grows exponentially with the number of tensors, but there are many heuristic algorithms available for computing optimal sequences for small networks[1][2][3][4][5][6]. ITensors.jl provides some functionality for helping you find the optimal contraction sequence for small tensor network, as we will show below.

The algorithm in ITensors.jl currently uses a modified version of[1] with simplifications for outer product contractions similar to those used in TensorOperations.jl.

Functions

ITensors.ContractionSequenceOptimization.contraction_costFunction
contraction_cost(A; sequence)

Return the cost of contracting the collection of ITensors according to the specified sequence, where the cost is measured in the number of floating point operations that would need to be performed to contract dense tensors of the dimensions specified by the indices of the tensors (so for now, sparsity is ignored in computing the costs). Pairwise costs are returned in a vector (contracting N tensors requires N-1 pairwise contractions). You can use sum(contraction_cost(A; sequence)) to get the total cost of the contraction.

If no sequence is specified, left associative contraction is used, in other words the sequence is equivalent to [[[[1, 2], 3], 4], …].

source
NDTensors.contractFunction
contract(ψ::MPS, A::MPO; kwargs...) -> MPS
 *(::MPS, ::MPO; kwargs...) -> MPS
 
 contract(A::MPO, ψ::MPS; kwargs...) -> MPS
-*(::MPO, ::MPS; kwargs...) -> MPS

Contract the MPO A with the MPS ψ, returning an MPS with the unique site indices of the MPO.

For example, for an MPO with site indices with prime levels of 1 and 0, such as -s'-A-s-, and an MPS with site indices with prime levels of 0, such as -s-x, the result is an MPS y with site indices with prime levels of 1, -s'-y = -s'-A-s-x.

Since it is common to contract an MPO with prime levels of 1 and 0 with an MPS with prime level of 0 and want a resulting MPS with prime levels of 0, we provide a convenience function apply:

apply(A, x; kwargs...) = replaceprime(contract(A, x; kwargs...), 2 => 1)`.

Choose the method with the method keyword, for example "densitymatrix" and "naive".

Keywords

  • cutoff::Float64=1e-13: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.
  • maxdim::Int=maxlinkdim(A) * maxlinkdim(ψ)): the maximal bond dimension of the results MPS.
  • mindim::Int=1: the minimal bond dimension of the resulting MPS.
  • normalize::Bool=false: whether or not to normalize the resulting MPS.
  • method::String="densitymatrix": the algorithm to use for the contraction. Currently the options are "densitymatrix", where the network formed by the MPO and MPS is squared and contracted down to a density matrix which is diagonalized iteratively at each site, and "naive", where the MPO and MPS tensor are contracted exactly at each site and then a truncation of the resulting MPS is performed.

See also apply.

source
contract(A::MPO, B::MPO; kwargs...) -> MPO
+*(::MPO, ::MPS; kwargs...) -> MPS

Contract the MPO A with the MPS ψ, returning an MPS with the unique site indices of the MPO.

For example, for an MPO with site indices with prime levels of 1 and 0, such as -s'-A-s-, and an MPS with site indices with prime levels of 0, such as -s-x, the result is an MPS y with site indices with prime levels of 1, -s'-y = -s'-A-s-x.

Since it is common to contract an MPO with prime levels of 1 and 0 with an MPS with prime level of 0 and want a resulting MPS with prime levels of 0, we provide a convenience function apply:

apply(A, x; kwargs...) = replaceprime(contract(A, x; kwargs...), 2 => 1)`.

Choose the method with the method keyword, for example "densitymatrix" and "naive".

Keywords

  • cutoff::Float64=1e-13: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.
  • maxdim::Int=maxlinkdim(A) * maxlinkdim(ψ)): the maximal bond dimension of the results MPS.
  • mindim::Int=1: the minimal bond dimension of the resulting MPS.
  • normalize::Bool=false: whether or not to normalize the resulting MPS.
  • method::String="densitymatrix": the algorithm to use for the contraction. Currently the options are "densitymatrix", where the network formed by the MPO and MPS is squared and contracted down to a density matrix which is diagonalized iteratively at each site, and "naive", where the MPO and MPS tensor are contracted exactly at each site and then a truncation of the resulting MPS is performed.

See also apply.

source
contract(A::MPO, B::MPO; kwargs...) -> MPO
 *(::MPO, ::MPO; kwargs...) -> MPO

Contract the MPO A with the MPO B, returning an MPO with the site indices that are not shared between A and B.

If you are contracting two MPOs with the same sets of indices, likely you want to call something like:

C = contract(A', B; cutoff=1e-12)
 C = replaceprime(C, 2 => 1)

That is because if MPO A has the index structure -s'-A-s- and MPO B has the Index structure -s'-B-s-, if we only want to contract over on set of the indices, we would do (-s'-A-s-)'-s'-B-s- = -s''-A-s'-s'-B-s- = -s''-C-s-, and then map the prime levels back to pairs of primed and unprimed indices with: replaceprime(-s''-C-s-, 2 => 1) = -s'-C-s-.

Since this is a common use case, you can use the convenience function:

C = apply(A, B; cutoff=1e-12)

which is the same as the code above.

If you are contracting MPOs that have diverging norms, such as MPOs representing sums of local operators, the truncation can become numerically unstable (see https://arxiv.org/abs/1909.06341 for a more numerically stable alternative). For now, you can use the following options to contract MPOs like that:

C = contract(A, B; alg="naive", truncate=false)
 # Bring the indices back to pairs of primed and unprimed
-C = apply(A, B; alg="naive", truncate=false)

Keywords

  • cutoff::Float64=1e-14: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.
  • maxdim::Int=maxlinkdim(A) * maxlinkdim(B)): the maximal bond dimension of the results MPS.
  • mindim::Int=1: the minimal bond dimension of the resulting MPS.
  • alg="zipup": Either "zipup" or "naive". "zipup" contracts pairs of site tensors and truncates with SVDs in a sweep across the sites, while "naive" first contracts pairs of tensor exactly and then truncates at the end if truncate=true.
  • truncate=true: Enable or disable truncation. If truncate=false, ignore other truncation parameters like cutoff and maxdim. This is most relevant for the "naive" version, if you just want to contract the tensors pairwise exactly. This can be useful if you are contracting MPOs that have diverging norms, such as MPOs originating from sums of local operators.

See also apply for details about the arguments available.

source
*(As::ITensor...; sequence = default_sequence(), kwargs...)
+C = apply(A, B; alg="naive", truncate=false)

Keywords

  • cutoff::Float64=1e-14: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.
  • maxdim::Int=maxlinkdim(A) * maxlinkdim(B)): the maximal bond dimension of the results MPS.
  • mindim::Int=1: the minimal bond dimension of the resulting MPS.
  • alg="zipup": Either "zipup" or "naive". "zipup" contracts pairs of site tensors and truncates with SVDs in a sweep across the sites, while "naive" first contracts pairs of tensor exactly and then truncates at the end if truncate=true.
  • truncate=true: Enable or disable truncation. If truncate=false, ignore other truncation parameters like cutoff and maxdim. This is most relevant for the "naive" version, if you just want to contract the tensors pairwise exactly. This can be useful if you are contracting MPOs that have diverging norms, such as MPOs originating from sums of local operators.

See also apply for details about the arguments available.

source
*(As::ITensor...; sequence = default_sequence(), kwargs...)
 *(As::Vector{<: ITensor}; sequence = default_sequence(), kwargs...)
-contract(As::ITensor...; sequence = default_sequence(), kwargs...)

Contract the set of ITensors according to the contraction sequence.

The default sequence is "automatic" if ITensors.using_contraction_sequence_optimization() is true, otherwise it is "left_associative" (the ITensors are contracted from left to right).

You can change the default with ITensors.enable_contraction_sequence_optimization() and ITensors.disable_contraction_sequence_optimization().

For a custom sequence, the sequence should be provided as a binary tree where the leaves are integers n specifying the ITensor As[n] and branches are accessed by indexing with 1 or 2, i.e. sequence = Any[Any[1, 3], Any[2, 4]].

source

Examples

In the following example we show how to compute the contraction sequence cost of a

using ITensors
+contract(As::ITensor...; sequence = default_sequence(), kwargs...)

Contract the set of ITensors according to the contraction sequence.

The default sequence is "automatic" if ITensors.using_contraction_sequence_optimization() is true, otherwise it is "left_associative" (the ITensors are contracted from left to right).

You can change the default with ITensors.enable_contraction_sequence_optimization() and ITensors.disable_contraction_sequence_optimization().

For a custom sequence, the sequence should be provided as a binary tree where the leaves are integers n specifying the ITensor As[n] and branches are accessed by indexing with 1 or 2, i.e. sequence = Any[Any[1, 3], Any[2, 4]].

source

Examples

In the following example we show how to compute the contraction sequence cost of a

using ITensors
 using Symbolics
 
 using ITensors: contraction_cost
@@ -103,4 +103,4 @@
 # Fix d to a certain value (such as 4 for a Hubbard site)
 @variables d
 var_sub = Dict(d => 4)
-display(substitute.(sum.(getindex.(sequence_costs, :symbolic_cost)), (var_sub,)))

A future direction will be to allow optimizing over contraction sequences with the dimensions specified symbolically, so that the optimal sequence in limits of certain dimensions can be found. In addition, we plan to implement more algorithms that work for larger networks, as well as algorithms like[2] which take an optimal sequence for a closed network and generate optimal sequences for environments of each tensor in the network, which is helpful for computing gradients of tensor networks.

+display(substitute.(sum.(getindex.(sequence_costs, :symbolic_cost)), (var_sub,)))

A future direction will be to allow optimizing over contraction sequences with the dimensions specified symbolically, so that the optimal sequence in limits of certain dimensions can be found. In addition, we plan to implement more algorithms that work for larger networks, as well as algorithms like[2] which take an optimal sequence for a closed network and generate optimal sequences for environments of each tensor in the network, which is helpful for computing gradients of tensor networks.

diff --git a/previews/PR1410/DMRG.html b/previews/PR1410/DMRG.html index 5dbe236eca..d12140a060 100644 --- a/previews/PR1410/DMRG.html +++ b/previews/PR1410/DMRG.html @@ -2,4 +2,4 @@ DMRG · ITensors.jl

DMRG

ITensors.ITensorMPS.dmrgFunction
dmrg(H::MPO, psi0::MPS; kwargs...)
 dmrg(H::MPO, psi0::MPS, sweeps::Sweeps; kwargs...)

Use the density matrix renormalization group (DMRG) algorithm to optimize a matrix product state (MPS) such that it is the eigenvector of lowest eigenvalue of a Hermitian matrix H, represented as a matrix product operator (MPO).

dmrg(Hs::Vector{MPO}, psi0::MPS; kwargs...)
 dmrg(Hs::Vector{MPO}, psi0::MPS, sweeps::Sweeps; kwargs...)

Use the density matrix renormalization group (DMRG) algorithm to optimize a matrix product state (MPS) such that it is the eigenvector of lowest eigenvalue of a Hermitian matrix H. This version of dmrg accepts a representation of H as a Vector of MPOs, Hs = [H1, H2, H3, ...] such that H is defined as H = H1 + H2 + H3 + ... Note that this sum of MPOs is not actually computed; rather the set of MPOs [H1,H2,H3,..] is efficiently looped over at each step of the DMRG algorithm when optimizing the MPS.

dmrg(H::MPO, Ms::Vector{MPS}, psi0::MPS; weight=1.0, kwargs...)
-dmrg(H::MPO, Ms::Vector{MPS}, psi0::MPS, sweeps::Sweeps; weight=1.0, kwargs...)

Use the density matrix renormalization group (DMRG) algorithm to optimize a matrix product state (MPS) such that it is the eigenvector of lowest eigenvalue of a Hermitian matrix H, subject to the constraint that the MPS is orthogonal to each of the MPS provided in the Vector Ms. The orthogonality constraint is approximately enforced by adding to H terms of the form w|M1><M1| + w|M2><M2| + ... where Ms=[M1, M2, ...] and w is the "weight" parameter, which can be adjusted through the optional weight keyword argument.

Note

dmrg will report the energy of the operator H + w|M1><M1| + w|M2><M2| + ..., not the operator H. If you want the expectation value of the MPS eigenstate with respect to just H, you can compute it yourself with an observer or after DMRG is run with inner(psi', H, psi).

The MPS psi0 is used to initialize the MPS to be optimized.

The number of sweeps of thd DMRG algorithm is controlled by passing the nsweeps keyword argument. The keyword arguments maxdim, cutoff, noise, and mindim can also be passed to control the cost versus accuracy of the algorithm - see below for details.

Alternatively the number of sweeps and accuracy parameters can be passed through a Sweeps object, though this interface is no longer preferred.

Returns:

  • energy::Number - eigenvalue of the optimized MPS
  • psi::MPS - optimized MPS

Keyword arguments:

  • nsweeps::Int - number of "sweeps" of DMRG to perform

Optional keyword arguments:

  • maxdim - integer or array of integers specifying the maximum size allowed for the bond dimension or rank of the MPS being optimized.
  • cutoff - float or array of floats specifying the truncation error cutoff or threshold to use for truncating the bond dimension or rank of the MPS.
  • eigsolve_krylovdim::Int = 3 - maximum dimension of Krylov space used to locally solve the eigenvalue problem. Try setting to a higher value if convergence is slow or the Hamiltonian is close to a critical point. [krylovkit]
  • eigsolve_tol::Number = 1e-14 - Krylov eigensolver tolerance. [krylovkit]
  • eigsolve_maxiter::Int = 1 - number of times the Krylov subspace can be rebuilt. [krylovkit]
  • eigsolve_verbosity::Int = 0 - verbosity level of the Krylov solver. Warning: enabling this will lead to a lot of outputs to the terminal. [krylovkit]
  • ishermitian=true - boolean specifying if dmrg should assume the MPO (or more general linear operator) represents a Hermitian matrix. [krylovkit]
  • noise - float or array of floats specifying strength of the "noise term" to use to aid convergence.
  • mindim - integer or array of integers specifying the minimum size of the bond dimension or rank, if possible.
  • outputlevel::Int = 1 - larger outputlevel values make DMRG print more information and 0 means no output.
  • observer - object implementing the Observer interface which can perform measurements and stop DMRG early.
  • write_when_maxdim_exceeds::Int - when the allowed maxdim exceeds this value, begin saving tensors to disk to free RAM memory in large calculations
  • write_path::String = tempdir() - path to use to save files to disk (to save RAM) when maxdim exceeds the write_when_maxdim_exceeds option, if set
source
  • krylovkitThe dmrg function in ITensors.jl currently uses the eigsolve function in KrylovKit.jl as the internal the eigensolver. See the KrylovKit.jl documention on the eigsolve function for more details: KrylovKit.eigsolve.
+dmrg(H::MPO, Ms::Vector{MPS}, psi0::MPS, sweeps::Sweeps; weight=1.0, kwargs...)

Use the density matrix renormalization group (DMRG) algorithm to optimize a matrix product state (MPS) such that it is the eigenvector of lowest eigenvalue of a Hermitian matrix H, subject to the constraint that the MPS is orthogonal to each of the MPS provided in the Vector Ms. The orthogonality constraint is approximately enforced by adding to H terms of the form w|M1><M1| + w|M2><M2| + ... where Ms=[M1, M2, ...] and w is the "weight" parameter, which can be adjusted through the optional weight keyword argument.

Note

dmrg will report the energy of the operator H + w|M1><M1| + w|M2><M2| + ..., not the operator H. If you want the expectation value of the MPS eigenstate with respect to just H, you can compute it yourself with an observer or after DMRG is run with inner(psi', H, psi).

The MPS psi0 is used to initialize the MPS to be optimized.

The number of sweeps of thd DMRG algorithm is controlled by passing the nsweeps keyword argument. The keyword arguments maxdim, cutoff, noise, and mindim can also be passed to control the cost versus accuracy of the algorithm - see below for details.

Alternatively the number of sweeps and accuracy parameters can be passed through a Sweeps object, though this interface is no longer preferred.

Returns:

Keyword arguments:

Optional keyword arguments:

source
diff --git a/previews/PR1410/DMRGObserver.html b/previews/PR1410/DMRGObserver.html index 8826a20f89..c03db4eb8b 100644 --- a/previews/PR1410/DMRGObserver.html +++ b/previews/PR1410/DMRGObserver.html @@ -7,8 +7,8 @@ println("Total Sz after sweep $sw = ", sum(Szs)/N) end

Constructors

ITensors.ITensorMPS.DMRGObserverMethod
DMRGObserver(;energy_tol=0.0,
               minsweeps=2,
-              energy_type=Float64)

Construct a DMRGObserver by providing the energy tolerance used for early stopping, and minimum number of sweeps that must be done.

Optional keyword arguments:

  • energy_tol: if the energy from one sweep to the next no longer changes by more than this amount, stop after the current sweep
  • minsweeps: do at least this many sweeps
  • energy_type: type to use when storing energies at each step
source
ITensors.ITensorMPS.DMRGObserverMethod
DMRGObserver(ops::Vector{String},
+              energy_type=Float64)

Construct a DMRGObserver by providing the energy tolerance used for early stopping, and minimum number of sweeps that must be done.

Optional keyword arguments:

  • energy_tol: if the energy from one sweep to the next no longer changes by more than this amount, stop after the current sweep
  • minsweeps: do at least this many sweeps
  • energy_type: type to use when storing energies at each step
source
ITensors.ITensorMPS.DMRGObserverMethod
DMRGObserver(ops::Vector{String},
              sites::Vector{<:Index};
              energy_tol=0.0,
              minsweeps=2,
-             energy_type=Float64)

Construct a DMRGObserver, provide an array of ops of operator names which are strings recognized by the op function. Each of these operators will be measured on every site during every step of DMRG and the results recorded inside the DMRGOberver for later analysis. The array sites is the basis of sites used to define the MPS and MPO for the DMRG calculation.

Optionally, one can provide an energy tolerance used for early stopping, and minimum number of sweeps that must be done.

Optional keyword arguments:

  • energy_tol: if the energy from one sweep to the next no longer changes by more than this amount, stop after the current sweep
  • minsweeps: do at least this many sweeps
  • energy_type: type to use when storing energies at each step
source

Methods

ITensors.ITensorMPS.measurementsMethod
measurements(o::DMRGObserver)

After using a DMRGObserver object o within a DMRG calculation, retrieve a dictionary of measurement results, with the keys being operator names and values being DMRGMeasurement objects.

source
ITensors.ITensorMPS.DMRGMeasurementType

A DMRGMeasurement object is an alias for Vector{Vector{Float64}}, in other words an array of arrays of real numbers.

Given a DMRGMeasurement M,the result for the measurement on sweep n and site i as M[n][i].

source
ITensors.ITensorMPS.energiesMethod
energies(o::DMRGObserver)

After using a DMRGObserver object o within a DMRG calculation, retrieve an array of the energy after each sweep.

source
+ energy_type=Float64)

Construct a DMRGObserver, provide an array of ops of operator names which are strings recognized by the op function. Each of these operators will be measured on every site during every step of DMRG and the results recorded inside the DMRGOberver for later analysis. The array sites is the basis of sites used to define the MPS and MPO for the DMRG calculation.

Optionally, one can provide an energy tolerance used for early stopping, and minimum number of sweeps that must be done.

Optional keyword arguments:

source

Methods

ITensors.ITensorMPS.measurementsMethod
measurements(o::DMRGObserver)

After using a DMRGObserver object o within a DMRG calculation, retrieve a dictionary of measurement results, with the keys being operator names and values being DMRGMeasurement objects.

source
ITensors.ITensorMPS.DMRGMeasurementType

A DMRGMeasurement object is an alias for Vector{Vector{Float64}}, in other words an array of arrays of real numbers.

Given a DMRGMeasurement M,the result for the measurement on sweep n and site i as M[n][i].

source
ITensors.ITensorMPS.energiesMethod
energies(o::DMRGObserver)

After using a DMRGObserver object o within a DMRG calculation, retrieve an array of the energy after each sweep.

source
diff --git a/previews/PR1410/DeveloperGuide.html b/previews/PR1410/DeveloperGuide.html index f3fde0e777..f982209d9a 100644 --- a/previews/PR1410/DeveloperGuide.html +++ b/previews/PR1410/DeveloperGuide.html @@ -29,4 +29,4 @@ # Call fA like this: fA(my_callback, psi; callback_args = (; a, b)) -
  • External (non-ITensor) Functions: Though it requires judgment in each case, if the keyword arguments an external (non-ITensor) function accepts are small in number, not expected to change, and known ahead of time, try to list them explicitly if possible (rather than forwarding with kwargs...). Possible exceptions could be if you want to make use of defaults defined for keyword arguments of an external function.

  • +
  • External (non-ITensor) Functions: Though it requires judgment in each case, if the keyword arguments an external (non-ITensor) function accepts are small in number, not expected to change, and known ahead of time, try to list them explicitly if possible (rather than forwarding with kwargs...). Possible exceptions could be if you want to make use of defaults defined for keyword arguments of an external function.

  • diff --git a/previews/PR1410/Einsum.html b/previews/PR1410/Einsum.html index f85fb1e410..d2e27ec826 100644 --- a/previews/PR1410/Einsum.html +++ b/previews/PR1410/Einsum.html @@ -1,23 +1,23 @@ -ITensor indices and Einstein notation · ITensors.jl

    ITensor Index identity: dimension labels and Einstein notation

    Many tensor contraction libraries use Einstein notation, such as NumPy's einsum function, ncon, and various Julia packages such as TensorOperations.jl, Tullio.jl, OMEinsum.jl, and Einsum.jl, among others.

    ITensor also uses Einstein notation, however the labels are stored inside the tensor and carried around with them during various operations. In addition, the labels that determine if tensor indices match with each other, and therefore automatically contract when doing * or match when adding or subtracting, are more sophisticated than simple characters or strings. ITensor indices are given a unique random ID number when they are constructed, and additionally users can add additional information like prime levels and tags which uniquely determine an Index. This is in contrast to simpler implementations of the same idea, such as the NamedDims.jl package, which only allow symbols as the metadata for uniquely identifying a tensor/array dimension.

    Index identity

    Here is an illustration of how the different types of Index metadata (random ID, prime level, and tags) work for Index identity:

    julia> i = Index(2)(dim=2|id=773)
    julia> j = Index(2)(dim=2|id=187)
    julia> i == jfalse
    julia> id(i)0xed3ab4e77ca4047d
    julia> id(j)0x76e112b6fcba9133
    julia> ip = i'(dim=2|id=773)'
    julia> ip == ifalse
    julia> plev(i) == 0true
    julia> plev(ip) == 1true
    julia> noprime(ip) == itrue
    julia> ix = addtags(i, "x")(dim=2|id=773|"x")
    julia> ix == ifalse
    julia> removetags(ix, "x") == itrue
    julia> ixyz = addtags(ix, "y,z")(dim=2|id=773|"x,y,z")
    julia> ixyz == addtags(i, "z,y,x")true

    The different metadata that are stored inside of ITensor indices that determine their identity are useful in different contexts. The random ID is particularly useful in the case when a new Index needs to be generated internally by ITensor, such as when performing a matrix factorization. In the case of a matrix factorization, we want to make sure that the new Index will not accidentally clash with an existing one, for example:

    julia> i = Index(2, "i")(dim=2|id=163|"i")
    julia> j = Index(2, "j")(dim=2|id=887|"j")
    julia> A = randomITensor(i, j)ITensor ord=2 (dim=2|id=163|"i") (dim=2|id=887|"j") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> U, S, V = svd(A, i; lefttags="i", righttags="j");
    julia> inds(U)((dim=2|id=163|"i"), (dim=2|id=663|"i"))
    julia> inds(S)((dim=2|id=663|"i"), (dim=2|id=847|"j"))
    julia> inds(V)((dim=2|id=887|"j"), (dim=2|id=847|"j"))
    julia> norm(U * S * V - A)2.7128146828510983e-15

    You can see that it would have been a problem here if there wasn't a new ID assigned to the Index, since it would have clashed with the original index. In this case, it could be avoided by giving the new indices different tags (with the keyword arguments lefttags and righttags), but in more complicated examples where it is not practical to do that (such as a case where many new indices are being introduced, for example for a tensor train (TT)/matrix product state (MPS)), it is convenient to not force users to come up with unique prime levels or tags themselves. It can also help to avoid accidental contractions in more complicated tensor network algorithms where there are many indices that can potentially have the same prime levels or tags.

    In contrast, using multiple indices with the same Index ID but different prime levels and tags can be useful in situations where there is a more fundamental relationship between the spaces. For example, in the case of an ITensor corresponding to a Hermitian operator, it is helpful to make the bra space and ket spaces the same up to a prime level:

    i = Index(2, "i")
    +ITensor indices and Einstein notation · ITensors.jl

    ITensor Index identity: dimension labels and Einstein notation

    Many tensor contraction libraries use Einstein notation, such as NumPy's einsum function, ncon, and various Julia packages such as TensorOperations.jl, Tullio.jl, OMEinsum.jl, and Einsum.jl, among others.

    ITensor also uses Einstein notation, however the labels are stored inside the tensor and carried around with them during various operations. In addition, the labels that determine if tensor indices match with each other, and therefore automatically contract when doing * or match when adding or subtracting, are more sophisticated than simple characters or strings. ITensor indices are given a unique random ID number when they are constructed, and additionally users can add additional information like prime levels and tags which uniquely determine an Index. This is in contrast to simpler implementations of the same idea, such as the NamedDims.jl package, which only allow symbols as the metadata for uniquely identifying a tensor/array dimension.

    Index identity

    Here is an illustration of how the different types of Index metadata (random ID, prime level, and tags) work for Index identity:

    julia> i = Index(2)(dim=2|id=956)
    julia> j = Index(2)(dim=2|id=376)
    julia> i == jfalse
    julia> id(i)0xc6aac38c58f025e4
    julia> id(j)0x4de6bb077607a028
    julia> ip = i'(dim=2|id=956)'
    julia> ip == ifalse
    julia> plev(i) == 0true
    julia> plev(ip) == 1true
    julia> noprime(ip) == itrue
    julia> ix = addtags(i, "x")(dim=2|id=956|"x")
    julia> ix == ifalse
    julia> removetags(ix, "x") == itrue
    julia> ixyz = addtags(ix, "y,z")(dim=2|id=956|"x,y,z")
    julia> ixyz == addtags(i, "z,y,x")true

    The different metadata that are stored inside of ITensor indices that determine their identity are useful in different contexts. The random ID is particularly useful in the case when a new Index needs to be generated internally by ITensor, such as when performing a matrix factorization. In the case of a matrix factorization, we want to make sure that the new Index will not accidentally clash with an existing one, for example:

    julia> i = Index(2, "i")(dim=2|id=158|"i")
    julia> j = Index(2, "j")(dim=2|id=279|"j")
    julia> A = randomITensor(i, j)ITensor ord=2 (dim=2|id=158|"i") (dim=2|id=279|"j") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> U, S, V = svd(A, i; lefttags="i", righttags="j");
    julia> inds(U)((dim=2|id=158|"i"), (dim=2|id=534|"i"))
    julia> inds(S)((dim=2|id=534|"i"), (dim=2|id=971|"j"))
    julia> inds(V)((dim=2|id=279|"j"), (dim=2|id=971|"j"))
    julia> norm(U * S * V - A)2.7128146828510983e-15

    You can see that it would have been a problem here if there wasn't a new ID assigned to the Index, since it would have clashed with the original index. In this case, it could be avoided by giving the new indices different tags (with the keyword arguments lefttags and righttags), but in more complicated examples where it is not practical to do that (such as a case where many new indices are being introduced, for example for a tensor train (TT)/matrix product state (MPS)), it is convenient to not force users to come up with unique prime levels or tags themselves. It can also help to avoid accidental contractions in more complicated tensor network algorithms where there are many indices that can potentially have the same prime levels or tags.

    In contrast, using multiple indices with the same Index ID but different prime levels and tags can be useful in situations where there is a more fundamental relationship between the spaces. For example, in the case of an ITensor corresponding to a Hermitian operator, it is helpful to make the bra space and ket spaces the same up to a prime level:

    i = Index(2, "i")
     j = Index(3, "j")
     A = randomITensor(i', j', dag(i), dag(j))
     H = 0.5 * (A + swapprime(dag(A), 0 => 1))
     v = randomITensor(i, j)
     Hv = noprime(H * v)
     vH = dag(v)' * H
    -norm(Hv - dag(vH))

    Note that we have added dag in a few places, which is superfluous in this case since the tensors are real and dense but become important when the tensors are complex and/or have symmetries. You can see that in this case, it is very useful to relate the bra and ket spaces by prime levels, since it makes it much easier to perform operations that map from one space to another. We could have created A from 4 entirely different indices with different ID numbers, but it would make the operations a bit more cumbersome, as shown below:

    julia> i = Index(2, "i")(dim=2|id=832|"i")
    julia> j = Index(3, "j")(dim=3|id=657|"j")
    julia> ip = Index(2, "i")(dim=2|id=472|"i")
    julia> jp = Index(3, "jp")(dim=3|id=997|"jp")
    julia> A = randomITensor(ip, jp, dag(i), dag(j))ITensor ord=4 (dim=2|id=472|"i") (dim=3|id=997|"jp") (dim=2|id=832|"i") (dim=3|id=657|"j") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> H = 0.5 * (A + swapinds(dag(A), (i, j), (ip, jp)))ITensor ord=4 (dim=2|id=472|"i") (dim=3|id=997|"jp") (dim=2|id=832|"i") (dim=3|id=657|"j") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> v = randomITensor(i, j)ITensor ord=2 (dim=2|id=832|"i") (dim=3|id=657|"j") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> Hv = replaceinds(H * v, (ip, jp) => (i, j))ITensor ord=2 (dim=2|id=832|"i") (dim=3|id=657|"j") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> vH = replaceinds(dag(v), (i, j) => (ip, jp)) * HITensor ord=2 (dim=2|id=832|"i") (dim=3|id=657|"j") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> norm(Hv - dag(vH))0.0

    Relationship to other Einstein notation-based libraries

    Here we show examples of different ways to perform the contraction "ab,bc,cd->ad" in ITensor.

    julia> da, dc = 2, 3;
    julia> db, dd = da, dc;
    julia> tags = ("a", "b", "c", "d");
    julia> dims = (da, db, dc, dd);
    julia> a, b, c, d = Index.(dims, tags);
    julia> Aab = randomITensor(a, b)ITensor ord=2 (dim=2|id=224|"a") (dim=2|id=746|"b") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> Bbc = randomITensor(b, c)ITensor ord=2 (dim=2|id=746|"b") (dim=3|id=892|"c") +norm(Hv - dag(vH))

    Note that we have added dag in a few places, which is superfluous in this case since the tensors are real and dense but become important when the tensors are complex and/or have symmetries. You can see that in this case, it is very useful to relate the bra and ket spaces by prime levels, since it makes it much easier to perform operations that map from one space to another. We could have created A from 4 entirely different indices with different ID numbers, but it would make the operations a bit more cumbersome, as shown below:

    julia> i = Index(2, "i")(dim=2|id=699|"i")
    julia> j = Index(3, "j")(dim=3|id=73|"j")
    julia> ip = Index(2, "i")(dim=2|id=842|"i")
    julia> jp = Index(3, "jp")(dim=3|id=148|"jp")
    julia> A = randomITensor(ip, jp, dag(i), dag(j))ITensor ord=4 (dim=2|id=842|"i") (dim=3|id=148|"jp") (dim=2|id=699|"i") (dim=3|id=73|"j") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> H = 0.5 * (A + swapinds(dag(A), (i, j), (ip, jp)))ITensor ord=4 (dim=2|id=842|"i") (dim=3|id=148|"jp") (dim=2|id=699|"i") (dim=3|id=73|"j") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> v = randomITensor(i, j)ITensor ord=2 (dim=2|id=699|"i") (dim=3|id=73|"j") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> Hv = replaceinds(H * v, (ip, jp) => (i, j))ITensor ord=2 (dim=2|id=699|"i") (dim=3|id=73|"j") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> vH = replaceinds(dag(v), (i, j) => (ip, jp)) * HITensor ord=2 (dim=2|id=699|"i") (dim=3|id=73|"j") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> norm(Hv - dag(vH))0.0

    Relationship to other Einstein notation-based libraries

    Here we show examples of different ways to perform the contraction "ab,bc,cd->ad" in ITensor.

    julia> da, dc = 2, 3;
    julia> db, dd = da, dc;
    julia> tags = ("a", "b", "c", "d");
    julia> dims = (da, db, dc, dd);
    julia> a, b, c, d = Index.(dims, tags);
    julia> Aab = randomITensor(a, b)ITensor ord=2 (dim=2|id=532|"a") (dim=2|id=99|"b") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> Bbc = randomITensor(b, c)ITensor ord=2 (dim=2|id=99|"b") (dim=3|id=81|"c") NDTensors.Dense{Float64, Vector{Float64}}
    julia> Ccd = randomITensor(c, d) - # "ab,bc,cd->ad"ITensor ord=2 (dim=3|id=892|"c") (dim=3|id=449|"d") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> out1 = Aab * Bbc * CcdITensor ord=2 (dim=2|id=224|"a") (dim=3|id=449|"d") + # "ab,bc,cd->ad"ITensor ord=2 (dim=3|id=81|"c") (dim=3|id=823|"d") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> out1 = Aab * Bbc * CcdITensor ord=2 (dim=2|id=532|"a") (dim=3|id=823|"d") NDTensors.Dense{Float64, Vector{Float64}}
    julia> @show hassameinds(out1, (a, d)) # @@ -25,9 +25,9 @@ # # "ba,bc,dc->ad"hassameinds(out1, (a, d)) = true -true
    julia> Aba = replaceinds(Aab, (a, b) => (b, a))ITensor ord=2 (dim=2|id=746|"b") (dim=2|id=224|"a") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> Cdc = replaceinds(Ccd, (c, d) => (d, c))ITensor ord=2 (dim=3|id=449|"d") (dim=3|id=892|"c") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> out2 = Aba * Bbc * CdcITensor ord=2 (dim=2|id=224|"a") (dim=3|id=449|"d") +true
    julia> Aba = replaceinds(Aab, (a, b) => (b, a))ITensor ord=2 (dim=2|id=99|"b") (dim=2|id=532|"a") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> Cdc = replaceinds(Ccd, (c, d) => (d, c))ITensor ord=2 (dim=3|id=823|"d") (dim=3|id=81|"c") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> out2 = Aba * Bbc * CdcITensor ord=2 (dim=2|id=532|"a") (dim=3|id=823|"d") NDTensors.Dense{Float64, Vector{Float64}}
    julia> @show hassameinds(out2, (a, d)) # @@ -38,9 +38,9 @@ # since it doesn't check if the indices # are compatible in dimension, # so is not recommended in general.hassameinds(out2, (a, d)) = true -true
    julia> using ITensors: setinds
    julia> Aba = setinds(Aab, (b, a))ITensor ord=2 (dim=2|id=746|"b") (dim=2|id=224|"a") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> Cdc = setinds(Ccd, (d, c))ITensor ord=2 (dim=3|id=449|"d") (dim=3|id=892|"c") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> out2 = Aba * Bbc * CdcITensor ord=2 (dim=2|id=224|"a") (dim=3|id=449|"d") +true
    julia> using ITensors: setinds
    julia> Aba = setinds(Aab, (b, a))ITensor ord=2 (dim=2|id=99|"b") (dim=2|id=532|"a") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> Cdc = setinds(Ccd, (d, c))ITensor ord=2 (dim=3|id=823|"d") (dim=3|id=81|"c") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> out2 = Aba * Bbc * CdcITensor ord=2 (dim=2|id=532|"a") (dim=3|id=823|"d") NDTensors.Dense{Float64, Vector{Float64}}
    julia> @show hassameinds(out2, (a, d)) # @@ -48,14 +48,14 @@ # the indices were made with these # prime levels in the first place) #hassameinds(out2, (a, d)) = true -true
    julia> a = Index(da, "a")(dim=2|id=289|"a")
    julia> c = Index(dc, "c")(dim=3|id=167|"c")
    julia> b, d = a', c'((dim=2|id=289|"a")', (dim=3|id=167|"c")')
    julia> Aab = randomITensor(a, b)ITensor ord=2 (dim=2|id=289|"a") (dim=2|id=289|"a")' -NDTensors.Dense{Float64, Vector{Float64}}
    julia> Bbc = randomITensor(b, c)ITensor ord=2 (dim=2|id=289|"a")' (dim=3|id=167|"c") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> Ccd = randomITensor(c, d)ITensor ord=2 (dim=3|id=167|"c") (dim=3|id=167|"c")' -NDTensors.Dense{Float64, Vector{Float64}}
    julia> out1 = Aab * Bbc * CcdITensor ord=2 (dim=2|id=289|"a") (dim=3|id=167|"c")' +true
    julia> a = Index(da, "a")(dim=2|id=131|"a")
    julia> c = Index(dc, "c")(dim=3|id=656|"c")
    julia> b, d = a', c'((dim=2|id=131|"a")', (dim=3|id=656|"c")')
    julia> Aab = randomITensor(a, b)ITensor ord=2 (dim=2|id=131|"a") (dim=2|id=131|"a")' +NDTensors.Dense{Float64, Vector{Float64}}
    julia> Bbc = randomITensor(b, c)ITensor ord=2 (dim=2|id=131|"a")' (dim=3|id=656|"c") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> Ccd = randomITensor(c, d)ITensor ord=2 (dim=3|id=656|"c") (dim=3|id=656|"c")' +NDTensors.Dense{Float64, Vector{Float64}}
    julia> out1 = Aab * Bbc * CcdITensor ord=2 (dim=2|id=131|"a") (dim=3|id=656|"c")' NDTensors.Dense{Float64, Vector{Float64}}
    julia> @show hassameinds(out1, (a, d))hassameinds(out1, (a, d)) = true -true
    julia> Aba = swapprime(Aab, 0 => 1)ITensor ord=2 (dim=2|id=289|"a")' (dim=2|id=289|"a") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> Cdc = swapprime(Ccd, 0 => 1)ITensor ord=2 (dim=3|id=167|"c")' (dim=3|id=167|"c") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> out2 = Aba * Bbc * CdcITensor ord=2 (dim=2|id=289|"a") (dim=3|id=167|"c")' +true
    julia> Aba = swapprime(Aab, 0 => 1)ITensor ord=2 (dim=2|id=131|"a")' (dim=2|id=131|"a") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> Cdc = swapprime(Ccd, 0 => 1)ITensor ord=2 (dim=3|id=656|"c")' (dim=3|id=656|"c") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> out2 = Aba * Bbc * CdcITensor ord=2 (dim=2|id=131|"a") (dim=3|id=656|"c")' NDTensors.Dense{Float64, Vector{Float64}}
    julia> @show hassameinds(out2, (a, d)) # @@ -63,14 +63,14 @@ # the indices were made with these # tags in the first place) #hassameinds(out2, (a, d)) = true -true
    julia> a = Index(da, "a")(dim=2|id=922|"a")
    julia> c = Index(dc, "c")(dim=3|id=384|"c")
    julia> b, d = settags(a, "b"), settags(c, "d")((dim=2|id=922|"b"), (dim=3|id=384|"d"))
    julia> Aab = randomITensor(a, b)ITensor ord=2 (dim=2|id=922|"a") (dim=2|id=922|"b") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> Bbc = randomITensor(b, c)ITensor ord=2 (dim=2|id=922|"b") (dim=3|id=384|"c") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> Ccd = randomITensor(c, d)ITensor ord=2 (dim=3|id=384|"c") (dim=3|id=384|"d") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> out1 = Aab * Bbc * CcdITensor ord=2 (dim=2|id=922|"a") (dim=3|id=384|"d") +true
    julia> a = Index(da, "a")(dim=2|id=651|"a")
    julia> c = Index(dc, "c")(dim=3|id=138|"c")
    julia> b, d = settags(a, "b"), settags(c, "d")((dim=2|id=651|"b"), (dim=3|id=138|"d"))
    julia> Aab = randomITensor(a, b)ITensor ord=2 (dim=2|id=651|"a") (dim=2|id=651|"b") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> Bbc = randomITensor(b, c)ITensor ord=2 (dim=2|id=651|"b") (dim=3|id=138|"c") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> Ccd = randomITensor(c, d)ITensor ord=2 (dim=3|id=138|"c") (dim=3|id=138|"d") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> out1 = Aab * Bbc * CcdITensor ord=2 (dim=2|id=651|"a") (dim=3|id=138|"d") NDTensors.Dense{Float64, Vector{Float64}}
    julia> @show hassameinds(out1, (a, d))hassameinds(out1, (a, d)) = true -true
    julia> Aba = swaptags(Aab, "a", "b")ITensor ord=2 (dim=2|id=922|"b") (dim=2|id=922|"a") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> Cdc = swaptags(Ccd, "c", "d")ITensor ord=2 (dim=3|id=384|"d") (dim=3|id=384|"c") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> out2 = Aba * Bbc * CdcITensor ord=2 (dim=2|id=922|"a") (dim=3|id=384|"d") +true
    julia> Aba = swaptags(Aab, "a", "b")ITensor ord=2 (dim=2|id=651|"b") (dim=2|id=651|"a") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> Cdc = swaptags(Ccd, "c", "d")ITensor ord=2 (dim=3|id=138|"d") (dim=3|id=138|"c") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> out2 = Aba * Bbc * CdcITensor ord=2 (dim=2|id=651|"a") (dim=3|id=138|"d") NDTensors.Dense{Float64, Vector{Float64}}
    julia> @show hassameinds(out2, (a, d)) # @@ -83,14 +83,14 @@ -1.26482 0.972584 0.0521089
    julia> C = randn(dc, dd)3×3 Matrix{Float64}: 0.073576 -0.572066 0.237091 -0.355748 -0.244972 0.662303 - 1.91204 1.69045 0.713253
    julia> tags = ("a", "b", "c", "d")("a", "b", "c", "d")
    julia> dims = (da, db, dc, dd)(2, 2, 3, 3)
    julia> a, b, c, d = Index.(dims, tags)((dim=2|id=718|"a"), (dim=2|id=379|"b"), (dim=3|id=901|"c"), (dim=3|id=77|"d"))
    julia> Aab = itensor(A, a, b)ITensor ord=2 (dim=2|id=718|"a") (dim=2|id=379|"b") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> Bbc = itensor(B, b, c)ITensor ord=2 (dim=2|id=379|"b") (dim=3|id=901|"c") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> Ccd = itensor(C, c, d)ITensor ord=2 (dim=3|id=901|"c") (dim=3|id=77|"d") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> out1 = Aab * Bbc * CcdITensor ord=2 (dim=2|id=718|"a") (dim=3|id=77|"d") + 1.91204 1.69045 0.713253
    julia> tags = ("a", "b", "c", "d")("a", "b", "c", "d")
    julia> dims = (da, db, dc, dd)(2, 2, 3, 3)
    julia> a, b, c, d = Index.(dims, tags)((dim=2|id=814|"a"), (dim=2|id=113|"b"), (dim=3|id=976|"c"), (dim=3|id=583|"d"))
    julia> Aab = itensor(A, a, b)ITensor ord=2 (dim=2|id=814|"a") (dim=2|id=113|"b") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> Bbc = itensor(B, b, c)ITensor ord=2 (dim=2|id=113|"b") (dim=3|id=976|"c") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> Ccd = itensor(C, c, d)ITensor ord=2 (dim=3|id=976|"c") (dim=3|id=583|"d") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> out1 = Aab * Bbc * CcdITensor ord=2 (dim=2|id=814|"a") (dim=3|id=583|"d") NDTensors.Dense{Float64, Vector{Float64}}
    julia> @show hassameinds(out1, (a, d))hassameinds(out1, (a, d)) = true -true
    julia> Aba = itensor(A, b, a)ITensor ord=2 (dim=2|id=379|"b") (dim=2|id=718|"a") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> Cdc = itensor(C, d, c)ITensor ord=2 (dim=3|id=77|"d") (dim=3|id=901|"c") -NDTensors.Dense{Float64, Vector{Float64}}
    julia> out2 = Aba * Bbc * CdcITensor ord=2 (dim=2|id=718|"a") (dim=3|id=77|"d") +true
    julia> Aba = itensor(A, b, a)ITensor ord=2 (dim=2|id=113|"b") (dim=2|id=814|"a") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> Cdc = itensor(C, d, c)ITensor ord=2 (dim=3|id=583|"d") (dim=3|id=976|"c") +NDTensors.Dense{Float64, Vector{Float64}}
    julia> out2 = Aba * Bbc * CdcITensor ord=2 (dim=2|id=814|"a") (dim=3|id=583|"d") NDTensors.Dense{Float64, Vector{Float64}}
    julia> @show hassameinds(out2, (a, d)) # @@ -103,4 +103,4 @@ # #out2 = A[b, a] * B[b, c] * C[d, c] #@show hassameinds(out2, (a, d))hassameinds(out2, (a, d)) = true -true
    +true
    diff --git a/previews/PR1410/HDF5FileFormats.html b/previews/PR1410/HDF5FileFormats.html index 8cd14e8e9b..72d4b4ff44 100644 --- a/previews/PR1410/HDF5FileFormats.html +++ b/previews/PR1410/HDF5FileFormats.html @@ -1,2 +1,2 @@ -HDF5 File Formats · ITensors.jl

    HDF5 File Formats

    This page lists the formats for the HDF5 representations of various types in the ITensors module.

    HDF5 is a portable file format which has a directory structure similar to a file system. In addition to containing "groups" (= directories) and "datasets" (= files), groups can have "attributes" appended to them, which are similar to 'tags' or 'keywords'. Unless otherwise specified, integers are 64 bit and are signed (H5T_STD_I64LE) unless explicitly stated. (For example, the "id" field of the Index type is stored as an unsigned 64 bit integer (H5T_STD_U64LE).)

    Each type in ITensor which is writeable to HDF5 is written to its own group, with the name of the group either specified by the user or specified to some default value when it is a subgroup of another ITensor type (for example, the Index type saves its TagSet in a subgroup named "tags").

    Each group corresponding to an ITensors type always carries the following attributes:

    • "type" –- a string such as Index or TagSet specifying the information necessary to determine the type of the object saved to the HDF5 group
    • "version" –- an integer specifying the file format version used to store the data. This version is in general different from the release version of ITensors.jl. The purpose of the version number is to aid in maintaining backwards compatibility, while allowing the format to be occasionally changed.

    The C++ version of ITensor uses exactly the same file formats listed below, for the purpose of interoperability with the Julia version of ITensor, even though conventions such as the "type" field values are Julia-centric.

    TagSet

    HDF5 file format for the ITensors.TagSet type.

    Attributes:

    • "version" = 1
    • "type" = "TagSet"

    Datasets and Subgroups:

    • "tags" [string] = a comma separated string of the tags in the TagSet

    QN

    HDF5 file format for the ITensors.QN type.

    Attributes:

    • "version" = 1
    • "type" = "QN"

    Datasets and Subgroups:

    • "names" [group] = array of strings (length 4) of names of quantum numbers
    • "vals" [group] = array of integers (length 4) of quantum number values
    • "mods" [group] = array of integers (length 4) of moduli of quantum numbers

    QNBlocks

    HDF5 file format for the ITensors.QNBlocks type. (Note: QNBlocks is equivalent to Vector{Pair{QN, Int64}}.)

    Attributes:

    • "version" = 1
    • "type" = "QNBlocks"

    Datasets and Subgroups:

    • "length" [integer] = the number of blocks (length of Vector)
    • "dims" [group] = array of (integer) dimensions of each block
    • "QN[n]" [group] = these groups "QN[1]", "QN[2]", etc. correspond to the QN of each block

    Index

    HDF5 file format for the ITensors.Index type.

    Attributes:

    • "version" = 1
    • "type" = "Index"
    • "space_type" = "Int" if the Index is a regular, dense Index or "QNBlocks" if the Index is a QNIndex (carries QN subspace information)

    Datasets and Subgroups:

    • "id" [unsigned integer] = id number of the Index
    • "dim" [integer] = dimension of the Index
    • "dir" [integer] = arrow direction of the Index, +1 for ITensors.Out and -1 for ITensors.In
    • "plev" [integer] = prime level of the Index
    • "tags" [group] = the TagSet of the Index

    Optional Datasets and Subgroups:

    • "space" [group] = if the "space_type" attribute is "QNBlocks", this group is present and represents a QNBlocks object

    IndexSet

    HDF5 file format for types in the Union type ITensors.Indices which includes IndexSet and tuples of Index objects.

    Attributes:

    • "version" = 1
    • "type" = "IndexSet"

    Datasets and Subgroups:

    • "length" [integer] = number of indices
    • "index_n" [group] = for n=1 to n=length each of these groups contains an Index

    ITensor

    HDF5 file format for the ITensors.ITensor type.

    Attributes:

    • "version" = 1
    • "type" = "ITensor"

    Datasets and Subgroups:

    • "inds" [group] = indices of the ITensor
    • "storage" [group] = storage of the ITensor (note that some earlier versions of ITensors.jl may call this group "store")

    NDTensors.Dense

    HDF5 file format for objects which are subtypes of ITensors.NDTensors.Dense.

    Attributes:

    • "version" = 1
    • "type" = "Dense{Float64}" or "Dense{ComplexF64}"

    Datasets and Subgroups:

    • "data" = array of either real or complex values (in the same dataset format used by the HDF5.jl library for storing Vector{Float64} or Vector{ComplexF64})

    NDTensors.BlockSparse

    HDF5 file format for objects which are subtypes of ITensors.NDTensors.BlockSparse.

    Attributes:

    • "version" = 1
    • "type" = "BlockSparse{Float64}" or "BlockSparse{ComplexF64}"

    Datasets and Subgroups:

    • "ndims" [integer] = number of dimensions (order) of the tensor
    • "offsets" = block offset data flattened into an array of integers
    • "data" = array of either real or complex values (in the same dataset format used by the HDF5.jl library for storing Vector{Float64} or Vector{ComplexF64})

    MPS

    HDF5 file format for ITensors.MPS

    Attributes:

    • "version" = 1
    • "type" = "MPS"

    Datasets and Subgroups:

    • "length" [integer] = number of tensors of the MPS
    • "rlim" [integer] = right orthogonality limit
    • "llim" [integer] = left orthogonality limit
    • "MPS[n]" [group,ITensor] = each of these groups, where n=1,...,length, stores the nth ITensor of the MPS

    MPO

    HDF5 file format for ITensors.MPO

    Attributes:

    • "version" = 1
    • "type" = "MPO"

    Datasets and Subgroups:

    • "length" [integer] = number of tensors of the MPO
    • "rlim" [integer] = right orthogonality limit
    • "llim" [integer] = left orthogonality limit
    • "MPO[n]" [group,ITensor] = each of these groups, where n=1,...,length, stores the nth ITensor of the MPO
    +HDF5 File Formats · ITensors.jl

    HDF5 File Formats

    This page lists the formats for the HDF5 representations of various types in the ITensors module.

    HDF5 is a portable file format which has a directory structure similar to a file system. In addition to containing "groups" (= directories) and "datasets" (= files), groups can have "attributes" appended to them, which are similar to 'tags' or 'keywords'. Unless otherwise specified, integers are 64 bit and are signed (H5T_STD_I64LE) unless explicitly stated. (For example, the "id" field of the Index type is stored as an unsigned 64 bit integer (H5T_STD_U64LE).)

    Each type in ITensor which is writeable to HDF5 is written to its own group, with the name of the group either specified by the user or specified to some default value when it is a subgroup of another ITensor type (for example, the Index type saves its TagSet in a subgroup named "tags").

    Each group corresponding to an ITensors type always carries the following attributes:

    • "type" –- a string such as Index or TagSet specifying the information necessary to determine the type of the object saved to the HDF5 group
    • "version" –- an integer specifying the file format version used to store the data. This version is in general different from the release version of ITensors.jl. The purpose of the version number is to aid in maintaining backwards compatibility, while allowing the format to be occasionally changed.

    The C++ version of ITensor uses exactly the same file formats listed below, for the purpose of interoperability with the Julia version of ITensor, even though conventions such as the "type" field values are Julia-centric.

    TagSet

    HDF5 file format for the ITensors.TagSet type.

    Attributes:

    • "version" = 1
    • "type" = "TagSet"

    Datasets and Subgroups:

    • "tags" [string] = a comma separated string of the tags in the TagSet

    QN

    HDF5 file format for the ITensors.QN type.

    Attributes:

    • "version" = 1
    • "type" = "QN"

    Datasets and Subgroups:

    • "names" [group] = array of strings (length 4) of names of quantum numbers
    • "vals" [group] = array of integers (length 4) of quantum number values
    • "mods" [group] = array of integers (length 4) of moduli of quantum numbers

    QNBlocks

    HDF5 file format for the ITensors.QNBlocks type. (Note: QNBlocks is equivalent to Vector{Pair{QN, Int64}}.)

    Attributes:

    • "version" = 1
    • "type" = "QNBlocks"

    Datasets and Subgroups:

    • "length" [integer] = the number of blocks (length of Vector)
    • "dims" [group] = array of (integer) dimensions of each block
    • "QN[n]" [group] = these groups "QN[1]", "QN[2]", etc. correspond to the QN of each block

    Index

    HDF5 file format for the ITensors.Index type.

    Attributes:

    • "version" = 1
    • "type" = "Index"
    • "space_type" = "Int" if the Index is a regular, dense Index or "QNBlocks" if the Index is a QNIndex (carries QN subspace information)

    Datasets and Subgroups:

    • "id" [unsigned integer] = id number of the Index
    • "dim" [integer] = dimension of the Index
    • "dir" [integer] = arrow direction of the Index, +1 for ITensors.Out and -1 for ITensors.In
    • "plev" [integer] = prime level of the Index
    • "tags" [group] = the TagSet of the Index

    Optional Datasets and Subgroups:

    • "space" [group] = if the "space_type" attribute is "QNBlocks", this group is present and represents a QNBlocks object

    IndexSet

    HDF5 file format for types in the Union type ITensors.Indices which includes IndexSet and tuples of Index objects.

    Attributes:

    • "version" = 1
    • "type" = "IndexSet"

    Datasets and Subgroups:

    • "length" [integer] = number of indices
    • "index_n" [group] = for n=1 to n=length each of these groups contains an Index

    ITensor

    HDF5 file format for the ITensors.ITensor type.

    Attributes:

    • "version" = 1
    • "type" = "ITensor"

    Datasets and Subgroups:

    • "inds" [group] = indices of the ITensor
    • "storage" [group] = storage of the ITensor (note that some earlier versions of ITensors.jl may call this group "store")

    NDTensors.Dense

    HDF5 file format for objects which are subtypes of ITensors.NDTensors.Dense.

    Attributes:

    • "version" = 1
    • "type" = "Dense{Float64}" or "Dense{ComplexF64}"

    Datasets and Subgroups:

    • "data" = array of either real or complex values (in the same dataset format used by the HDF5.jl library for storing Vector{Float64} or Vector{ComplexF64})

    NDTensors.BlockSparse

    HDF5 file format for objects which are subtypes of ITensors.NDTensors.BlockSparse.

    Attributes:

    • "version" = 1
    • "type" = "BlockSparse{Float64}" or "BlockSparse{ComplexF64}"

    Datasets and Subgroups:

    • "ndims" [integer] = number of dimensions (order) of the tensor
    • "offsets" = block offset data flattened into an array of integers
    • "data" = array of either real or complex values (in the same dataset format used by the HDF5.jl library for storing Vector{Float64} or Vector{ComplexF64})

    MPS

    HDF5 file format for ITensors.MPS

    Attributes:

    • "version" = 1
    • "type" = "MPS"

    Datasets and Subgroups:

    • "length" [integer] = number of tensors of the MPS
    • "rlim" [integer] = right orthogonality limit
    • "llim" [integer] = left orthogonality limit
    • "MPS[n]" [group,ITensor] = each of these groups, where n=1,...,length, stores the nth ITensor of the MPS

    MPO

    HDF5 file format for ITensors.MPO

    Attributes:

    • "version" = 1
    • "type" = "MPO"

    Datasets and Subgroups:

    • "length" [integer] = number of tensors of the MPO
    • "rlim" [integer] = right orthogonality limit
    • "llim" [integer] = left orthogonality limit
    • "MPO[n]" [group,ITensor] = each of these groups, where n=1,...,length, stores the nth ITensor of the MPO
    diff --git a/previews/PR1410/ITensorType.html b/previews/PR1410/ITensorType.html index 4d48386722..34a581a238 100644 --- a/previews/PR1410/ITensorType.html +++ b/previews/PR1410/ITensorType.html @@ -60,20 +60,20 @@ NDTensors.Dense{Float64,Array{Float64,1}} 2×2 -0.3674957028513448 1.6904886171664615 - 1.2579101497658178 -1.3559959053693322source

    Dense Constructors

    ITensors.ITensorMethod
    ITensor([::Type{ElT} = Float64, ]inds)
    +  1.2579101497658178  -1.3559959053693322
    source

    Dense Constructors

    ITensors.ITensorMethod
    ITensor([::Type{ElT} = Float64, ]inds)
     ITensor([::Type{ElT} = Float64, ]inds::Index...)

    Construct an ITensor filled with zeros having indices inds and element type ElT. If the element type is not specified, it defaults to Float64.

    The storage will have NDTensors.Dense type.

    Examples

    i = Index(2,"index_i")
     j = Index(4,"index_j")
     k = Index(3,"index_k")
     
     A = ITensor(i,j)
    -B = ITensor(ComplexF64,k,j)
    source
    ITensors.ITensorMethod
    ITensor([::Type{ElT} = Float64, ]::UndefInitializer, inds)
    +B = ITensor(ComplexF64,k,j)
    source
    ITensors.ITensorMethod
    ITensor([::Type{ElT} = Float64, ]::UndefInitializer, inds)
     ITensor([::Type{ElT} = Float64, ]::UndefInitializer, inds::Index...)

    Construct an ITensor filled with undefined elements having indices inds and element type ElT. If the element type is not specified, it defaults to Float64. One purpose for using this constructor is that initializing the elements in an undefined way is faster than initializing them to a set value such as zero.

    The storage will have NDTensors.Dense type.

    Examples

    i = Index(2,"index_i")
     j = Index(4,"index_j")
     k = Index(3,"index_k")
     
     A = ITensor(undef,i,j)
    -B = ITensor(ComplexF64,undef,k,j)
    source
    ITensors.ITensorMethod
    ITensor([ElT::Type, ]x::Number, inds)
    -ITensor([ElT::Type, ]x::Number, inds::Index...)

    Construct an ITensor with all elements set to x and indices inds.

    If x isa Int or x isa Complex{Int} then the elements will be set to float(x) unless specified otherwise by the first input.

    The storage will have NDTensors.Dense type.

    Examples

    ```julia i = Index(2,"indexi"); j = Index(4,"indexj"); k = Index(3,"index_k");

    A = ITensor(1.0, i, j) A = ITensor(1, i, j) # same as above B = ITensor(2.0+3.0im, j, k) ```

    !!! warning In future versions this may not automatically convert integer inputs with float, and in that case the particular element type should not be relied on.

    source
    ITensors.ITensorMethod
    ITensor([ElT::Type, ]A::Array, inds)
    +B = ITensor(ComplexF64,undef,k,j)
    source
    ITensors.ITensorMethod
    ITensor([ElT::Type, ]x::Number, inds)
    +ITensor([ElT::Type, ]x::Number, inds::Index...)

    Construct an ITensor with all elements set to x and indices inds.

    If x isa Int or x isa Complex{Int} then the elements will be set to float(x) unless specified otherwise by the first input.

    The storage will have NDTensors.Dense type.

    Examples

    ```julia i = Index(2,"indexi"); j = Index(4,"indexj"); k = Index(3,"index_k");

    A = ITensor(1.0, i, j) A = ITensor(1, i, j) # same as above B = ITensor(2.0+3.0im, j, k) ```

    !!! warning In future versions this may not automatically convert integer inputs with float, and in that case the particular element type should not be relied on.

    source
    ITensors.ITensorMethod
    ITensor([ElT::Type, ]A::Array, inds)
     ITensor([ElT::Type, ]A::Array, inds::Index...)
     
     itensor([ElT::Type, ]A::Array, inds)
    @@ -87,13 +87,13 @@
     T = ITensor(M, i, j)
     T[i => 1, j => 1] = 3.3
     M[1, 1] == 3.3
    -T[i => 1, j => 1] == 3.3
    Warning

    In future versions this may not automatically convert Int/Complex{Int} inputs to floating point versions with float (once tensor operations using Int/Complex{Int} are natively as fast as floating point operations), and in that case the particular element type should not be relied on. To avoid extra conversions (and therefore allocations) it is best practice to directly construct with itensor([0. 1; 1 0], i', dag(i)) if you want a floating point element type. The conversion is done as a performance optimization since often tensors are passed to BLAS/LAPACK and need to be converted to floating point types compatible with those libraries, but future projects in Julia may allow for efficient operations with more general element types (for example see https://github.com/JuliaLinearAlgebra/Octavian.jl).

    source
    ITensors.randomITensorMethod
    randomITensor([::Type{ElT <: Number} = Float64, ]inds)
    +T[i => 1, j => 1] == 3.3
    Warning

    In future versions this may not automatically convert Int/Complex{Int} inputs to floating point versions with float (once tensor operations using Int/Complex{Int} are natively as fast as floating point operations), and in that case the particular element type should not be relied on. To avoid extra conversions (and therefore allocations) it is best practice to directly construct with itensor([0. 1; 1 0], i', dag(i)) if you want a floating point element type. The conversion is done as a performance optimization since often tensors are passed to BLAS/LAPACK and need to be converted to floating point types compatible with those libraries, but future projects in Julia may allow for efficient operations with more general element types (for example see https://github.com/JuliaLinearAlgebra/Octavian.jl).

    source
    ITensors.randomITensorMethod
    randomITensor([::Type{ElT <: Number} = Float64, ]inds)
     randomITensor([::Type{ElT <: Number} = Float64, ]inds::Index...)

    Construct an ITensor with type ElT and indices inds, whose elements are normally distributed random numbers. If the element type is not specified, it defaults to Float64.

    Examples

    i = Index(2,"index_i")
     j = Index(4,"index_j")
     k = Index(3,"index_k")
     
     A = randomITensor(i,j)
    -B = randomITensor(ComplexF64,undef,k,j)
    source
    ITensors.onehotFunction
    onehot(ivs...)
    +B = randomITensor(ComplexF64,undef,k,j)
    source
    ITensors.onehotFunction
    onehot(ivs...)
     setelt(ivs...)
     onehot(::Type, ivs...)
     setelt(::Type, ivs...)

    Create an ITensor with all zeros except the specified value, which is set to 1.

    Examples

    i = Index(2,"i")
    @@ -105,7 +105,7 @@
     
     j = Index(3,"j")
     B = onehot(i=>1,j=>3)
    -# B[i=>1,j=>3] == 1, all other element zero
    source

    Dense View Constructors

    ITensors.itensorMethod
    itensor(args...; kwargs...)

    Like the ITensor constructor, but with attempt to make a view of the input data when possible.

    source

    QN BlockSparse Constructors

    ITensors.ITensorMethod
    ITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]inds)
    +# B[i=>1,j=>3] == 1, all other element zero
    source

    Dense View Constructors

    ITensors.itensorMethod
    itensor(args...; kwargs...)

    Like the ITensor constructor, but with attempt to make a view of the input data when possible.

    source

    QN BlockSparse Constructors

    ITensors.ITensorMethod
    ITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]inds)
     ITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]inds::Index...)

    Construct an ITensor with BlockSparse storage filled with zero(ElT) where the nonzero blocks are determined by flux.

    If ElT is not specified it defaults to Float64.

    If flux is not specified, the ITensor will be empty (it will contain no blocks, and have an undefined flux). The flux will be set by the first element that is set.

    Examples

    julia> i
     (dim=3|id=212|"i") <Out>
      1: QN(0) => 1
    @@ -209,7 +209,7 @@
      2  0
     
     julia> flux(A)
    -QN(-1)
    source
    ITensors.ITensorMethod
    ITensor([ElT::Type, ]A::Array, inds)
    +QN(-1)
    source
    ITensors.ITensorMethod
    ITensor([ElT::Type, ]A::Array, inds)
     ITensor([ElT::Type, ]A::Array, inds::Index...)
     
     itensor([ElT::Type, ]A::Array, inds)
    @@ -223,7 +223,7 @@
     T = ITensor(M, i, j)
     T[i => 1, j => 1] = 3.3
     M[1, 1] == 3.3
    -T[i => 1, j => 1] == 3.3
    Warning

    In future versions this may not automatically convert Int/Complex{Int} inputs to floating point versions with float (once tensor operations using Int/Complex{Int} are natively as fast as floating point operations), and in that case the particular element type should not be relied on. To avoid extra conversions (and therefore allocations) it is best practice to directly construct with itensor([0. 1; 1 0], i', dag(i)) if you want a floating point element type. The conversion is done as a performance optimization since often tensors are passed to BLAS/LAPACK and need to be converted to floating point types compatible with those libraries, but future projects in Julia may allow for efficient operations with more general element types (for example see https://github.com/JuliaLinearAlgebra/Octavian.jl).

    source
    ITensor([ElT::Type, ]::AbstractArray, inds; tol=0.0, checkflux=true)

    Create a block sparse ITensor from the input Array, and collection of QN indices. Zeros are dropped and nonzero blocks are determined from the zero values of the array.

    Optionally, you can set a tolerance such that elements less than or equal to the tolerance are dropped.

    By default, this will check that the flux of the nonzero blocks are consistent with each other. You can disable this check by setting checkflux=false.

    Examples

    julia> i = Index([QN(0)=>1, QN(1)=>2], "i");
    +T[i => 1, j => 1] == 3.3
    Warning

    In future versions this may not automatically convert Int/Complex{Int} inputs to floating point versions with float (once tensor operations using Int/Complex{Int} are natively as fast as floating point operations), and in that case the particular element type should not be relied on. To avoid extra conversions (and therefore allocations) it is best practice to directly construct with itensor([0. 1; 1 0], i', dag(i)) if you want a floating point element type. The conversion is done as a performance optimization since often tensors are passed to BLAS/LAPACK and need to be converted to floating point types compatible with those libraries, but future projects in Julia may allow for efficient operations with more general element types (for example see https://github.com/JuliaLinearAlgebra/Octavian.jl).

    source
    ITensor([ElT::Type, ]::AbstractArray, inds; tol=0.0, checkflux=true)

    Create a block sparse ITensor from the input Array, and collection of QN indices. Zeros are dropped and nonzero blocks are determined from the zero values of the array.

    Optionally, you can set a tolerance such that elements less than or equal to the tolerance are dropped.

    By default, this will check that the flux of the nonzero blocks are consistent with each other. You can disable this check by setting checkflux=false.

    Examples

    julia> i = Index([QN(0)=>1, QN(1)=>2], "i");
     
     julia> A = [1e-9 0.0 0.0;
                 0.0 2.0 3.0;
    @@ -242,17 +242,17 @@
     Block: (2, 2)
      [2:3, 2:3]
      2.0  3.0
    - 0.0  4.0
    source
    ITensors.ITensorMethod
    ITensor([::Type{ElT} = Float64,] ::UndefInitializer, flux::QN, inds)
    + 0.0  4.0
    source
    ITensors.ITensorMethod
    ITensor([::Type{ElT} = Float64,] ::UndefInitializer, flux::QN, inds)
     ITensor([::Type{ElT} = Float64,] ::UndefInitializer, flux::QN, inds::Index...)

    Construct an ITensor with indices inds and BlockSparse storage with undefined elements of type ElT, where the nonzero (allocated) blocks are determined by the provided QN flux. One purpose for using this constructor is that initializing the elements in an undefined way is faster than initializing them to a set value such as zero.

    The storage will have NDTensors.BlockSparse type.

    Examples

    i = Index([QN(0)=>1, QN(1)=>2], "i")
     A = ITensor(undef,QN(0),i',dag(i))
     B = ITensor(Float64,undef,QN(0),i',dag(i))
    -C = ITensor(ComplexF64,undef,QN(0),i',dag(i))
    source

    Diagonal constructors

    ITensors.diagITensorMethod
    diagITensor([::Type{ElT} = Float64, ]inds)
    -diagITensor([::Type{ElT} = Float64, ]inds::Index...)

    Make a sparse ITensor of element type ElT with only elements along the diagonal stored. Defaults to having zero(T) along the diagonal.

    The storage will have NDTensors.Diag type.

    source
    ITensors.diagITensorMethod
    diagITensor([ElT::Type, ]v::Vector, inds...)
    -diagitensor([ElT::Type, ]v::Vector, inds...)

    Make a sparse ITensor with non-zero elements only along the diagonal. In general, the diagonal elements will be those stored in v and the ITensor will have element type eltype(v), unless specified explicitly by ElT. The storage will have NDTensors.Diag type.

    In the case when eltype(v) isa Union{Int, Complex{Int}}, by default it will be converted to float(v). Note that this behavior is subject to change in the future.

    The version diagITensor will never output an ITensor whose storage data is an alias of the input vector data.

    The version diagitensor might output an ITensor whose storage data is an alias of the input vector data in order to minimize operations.

    source
    ITensors.diagITensorMethod
    diagITensor([ElT::Type, ]x::Number, inds...)
    -diagitensor([ElT::Type, ]x::Number, inds...)

    Make a sparse ITensor with non-zero elements only along the diagonal. In general, the diagonal elements will be set to the value x and the ITensor will have element type eltype(x), unless specified explicitly by ElT. The storage will have NDTensors.Diag type.

    In the case when x isa Union{Int, Complex{Int}}, by default it will be converted to float(x). Note that this behavior is subject to change in the future.

    source
    ITensors.deltaMethod
    delta([::Type{ElT} = Float64, ]inds)
    -delta([::Type{ElT} = Float64, ]inds::Index...)

    Make a uniform diagonal ITensor with all diagonal elements one(ElT). Only a single diagonal element is stored.

    This function has an alias δ.

    source

    QN Diagonal constructors

    ITensors.diagITensorMethod
    diagITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]is)
    -diagITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]is::Index...)

    Make an ITensor with storage type NDTensors.DiagBlockSparse with elements zero(ElT). The ITensor only has diagonal blocks consistent with the specified flux.

    If the element type is not specified, it defaults to Float64. If theflux is not specified, it defaults to QN().

    source
    ITensors.deltaMethod
    delta([::Type{ElT} = Float64, ][flux::QN = QN(), ]is)
    -delta([::Type{ElT} = Float64, ][flux::QN = QN(), ]is::Index...)

    Make an ITensor with storage type NDTensors.DiagBlockSparse with uniform elements one(ElT). The ITensor only has diagonal blocks consistent with the specified flux.

    If the element type is not specified, it defaults to Float64. If theflux is not specified, it defaults to QN().

    source

    Convert to Array

    Core.ArrayMethod
    Array{ElT, N}(T::ITensor, i:Index...)
    +C = ITensor(ComplexF64,undef,QN(0),i',dag(i))
    source

    Diagonal constructors

    ITensors.diagITensorMethod
    diagITensor([::Type{ElT} = Float64, ]inds)
    +diagITensor([::Type{ElT} = Float64, ]inds::Index...)

    Make a sparse ITensor of element type ElT with only elements along the diagonal stored. Defaults to having zero(T) along the diagonal.

    The storage will have NDTensors.Diag type.

    source
    ITensors.diagITensorMethod
    diagITensor([ElT::Type, ]v::Vector, inds...)
    +diagitensor([ElT::Type, ]v::Vector, inds...)

    Make a sparse ITensor with non-zero elements only along the diagonal. In general, the diagonal elements will be those stored in v and the ITensor will have element type eltype(v), unless specified explicitly by ElT. The storage will have NDTensors.Diag type.

    In the case when eltype(v) isa Union{Int, Complex{Int}}, by default it will be converted to float(v). Note that this behavior is subject to change in the future.

    The version diagITensor will never output an ITensor whose storage data is an alias of the input vector data.

    The version diagitensor might output an ITensor whose storage data is an alias of the input vector data in order to minimize operations.

    source
    ITensors.diagITensorMethod
    diagITensor([ElT::Type, ]x::Number, inds...)
    +diagitensor([ElT::Type, ]x::Number, inds...)

    Make a sparse ITensor with non-zero elements only along the diagonal. In general, the diagonal elements will be set to the value x and the ITensor will have element type eltype(x), unless specified explicitly by ElT. The storage will have NDTensors.Diag type.

    In the case when x isa Union{Int, Complex{Int}}, by default it will be converted to float(x). Note that this behavior is subject to change in the future.

    source
    ITensors.deltaMethod
    delta([::Type{ElT} = Float64, ]inds)
    +delta([::Type{ElT} = Float64, ]inds::Index...)

    Make a uniform diagonal ITensor with all diagonal elements one(ElT). Only a single diagonal element is stored.

    This function has an alias δ.

    source

    QN Diagonal constructors

    ITensors.diagITensorMethod
    diagITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]is)
    +diagITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]is::Index...)

    Make an ITensor with storage type NDTensors.DiagBlockSparse with elements zero(ElT). The ITensor only has diagonal blocks consistent with the specified flux.

    If the element type is not specified, it defaults to Float64. If theflux is not specified, it defaults to QN().

    source
    ITensors.deltaMethod
    delta([::Type{ElT} = Float64, ][flux::QN = QN(), ]is)
    +delta([::Type{ElT} = Float64, ][flux::QN = QN(), ]is::Index...)

    Make an ITensor with storage type NDTensors.DiagBlockSparse with uniform elements one(ElT). The ITensor only has diagonal blocks consistent with the specified flux.

    If the element type is not specified, it defaults to Float64. If theflux is not specified, it defaults to QN().

    source

    Convert to Array

    Core.ArrayMethod
    Array{ElT, N}(T::ITensor, i:Index...)
     Array{ElT}(T::ITensor, i:Index...)
     Array(T::ITensor, i:Index...)
     
    @@ -260,9 +260,9 @@
     Matrix(T::ITensor, row_i:Index, col_i::Index)
     
     Vector{ElT}(T::ITensor)
    -Vector(T::ITensor)

    Given an ITensor T with indices i..., returns an Array with a copy of the ITensor's elements. The order in which the indices are provided indicates the order of the data in the resulting Array.

    source
    NDTensors.arrayMethod
    array(T::ITensor, inds...)

    Convert an ITensor T to an Array.

    The ordering of the elements in the Array are specified by the input indices inds. This tries to avoid copying of possible (i.e. may return a view of the original data), for example if the ITensor's storage is Dense and the indices are already in the specified ordering so that no permutation is required.

    Warning

    Note that in the future we may return specialized AbstractArray types for certain storage types, for example a LinearAlgebra.Diagonal type for an ITensor with Diag storage. The specific storage type shouldn't be relied upon.

    See also matrix, vector.

    source
    NDTensors.matrixMethod
    matrix(T::ITensor, inds...)

    Convert an ITensor T to a Matrix.

    The ordering of the elements in the Matrix are specified by the input indices inds. This tries to avoid copying of possible (i.e. may return a view of the original data), for example if the ITensor's storage is Dense and the indices are already in the specified ordering so that no permutation is required.

    Warning

    Note that in the future we may return specialized AbstractArray types for certain storage types, for example a LinearAlgebra.Diagonal type for an ITensor with Diag storage. The specific storage type shouldn't be relied upon.

    See also array, vector.

    source
    NDTensors.vectorMethod
    vector(T::ITensor, inds...)

    Convert an ITensor T to an Vector.

    The ordering of the elements in the Array are specified by the input indices inds. This tries to avoid copying of possible (i.e. may return a view of the original data), for example if the ITensor's storage is Dense and the indices are already in the specified ordering so that no permutation is required.

    Warning

    Note that in the future we may return specialized AbstractArray types for certain storage types, for example a LinearAlgebra.Diagonal type for an ITensor with Diag storage. The specific storage type shouldn't be relied upon.

    See also array, matrix.

    source
    NDTensors.arrayMethod
    array(T::ITensor)

    Given an ITensor T, returns an Array with a copy of the ITensor's elements, or a view in the case the the ITensor's storage is Dense.

    The ordering of the elements in the Array, in terms of which Index is treated as the row versus column, depends on the internal layout of the ITensor.

    Warning

    This method is intended for developer use only and not recommended for use in ITensor applications unless you know what you are doing (for example you are certain of the memory ordering of the ITensor because you permuted the indices into a certain order).

    See also matrix, vector.

    source
    NDTensors.matrixMethod
    matrix(T::ITensor)

    Given an ITensor T with two indices, returns a Matrix with a copy of the ITensor's elements, or a view in the case the ITensor's storage is Dense.

    The ordering of the elements in the Matrix, in terms of which Index is treated as the row versus column, depends on the internal layout of the ITensor.

    Warning

    This method is intended for developer use only and not recommended for use in ITensor applications unless you know what you are doing (for example you are certain of the memory ordering of the ITensor because you permuted the indices into a certain order).

    See also array, vector.

    source
    NDTensors.vectorMethod
    vector(T::ITensor)

    Given an ITensor T with one index, returns a Vector with a copy of the ITensor's elements, or a view in the case the ITensor's storage is Dense.

    See also array, matrix.

    source

    Getting and setting elements

    Base.getindexMethod
    getindex(T::ITensor, ivs...)

    Get the specified element of the ITensor, using a list of IndexVals or Pair{<:Index, Int}.

    Example

    i = Index(2; tags = "i")
    +Vector(T::ITensor)

    Given an ITensor T with indices i..., returns an Array with a copy of the ITensor's elements. The order in which the indices are provided indicates the order of the data in the resulting Array.

    source
    NDTensors.arrayMethod
    array(T::ITensor, inds...)

    Convert an ITensor T to an Array.

    The ordering of the elements in the Array are specified by the input indices inds. This tries to avoid copying of possible (i.e. may return a view of the original data), for example if the ITensor's storage is Dense and the indices are already in the specified ordering so that no permutation is required.

    Warning

    Note that in the future we may return specialized AbstractArray types for certain storage types, for example a LinearAlgebra.Diagonal type for an ITensor with Diag storage. The specific storage type shouldn't be relied upon.

    See also matrix, vector.

    source
    NDTensors.matrixMethod
    matrix(T::ITensor, inds...)

    Convert an ITensor T to a Matrix.

    The ordering of the elements in the Matrix are specified by the input indices inds. This tries to avoid copying of possible (i.e. may return a view of the original data), for example if the ITensor's storage is Dense and the indices are already in the specified ordering so that no permutation is required.

    Warning

    Note that in the future we may return specialized AbstractArray types for certain storage types, for example a LinearAlgebra.Diagonal type for an ITensor with Diag storage. The specific storage type shouldn't be relied upon.

    See also array, vector.

    source
    NDTensors.vectorMethod
    vector(T::ITensor, inds...)

    Convert an ITensor T to an Vector.

    The ordering of the elements in the Array are specified by the input indices inds. This tries to avoid copying of possible (i.e. may return a view of the original data), for example if the ITensor's storage is Dense and the indices are already in the specified ordering so that no permutation is required.

    Warning

    Note that in the future we may return specialized AbstractArray types for certain storage types, for example a LinearAlgebra.Diagonal type for an ITensor with Diag storage. The specific storage type shouldn't be relied upon.

    See also array, matrix.

    source
    NDTensors.arrayMethod
    array(T::ITensor)

    Given an ITensor T, returns an Array with a copy of the ITensor's elements, or a view in the case the the ITensor's storage is Dense.

    The ordering of the elements in the Array, in terms of which Index is treated as the row versus column, depends on the internal layout of the ITensor.

    Warning

    This method is intended for developer use only and not recommended for use in ITensor applications unless you know what you are doing (for example you are certain of the memory ordering of the ITensor because you permuted the indices into a certain order).

    See also matrix, vector.

    source
    NDTensors.matrixMethod
    matrix(T::ITensor)

    Given an ITensor T with two indices, returns a Matrix with a copy of the ITensor's elements, or a view in the case the ITensor's storage is Dense.

    The ordering of the elements in the Matrix, in terms of which Index is treated as the row versus column, depends on the internal layout of the ITensor.

    Warning

    This method is intended for developer use only and not recommended for use in ITensor applications unless you know what you are doing (for example you are certain of the memory ordering of the ITensor because you permuted the indices into a certain order).

    See also array, vector.

    source
    NDTensors.vectorMethod
    vector(T::ITensor)

    Given an ITensor T with one index, returns a Vector with a copy of the ITensor's elements, or a view in the case the ITensor's storage is Dense.

    See also array, matrix.

    source

    Getting and setting elements

    Base.getindexMethod
    getindex(T::ITensor, ivs...)

    Get the specified element of the ITensor, using a list of IndexVals or Pair{<:Index, Int}.

    Example

    i = Index(2; tags = "i")
     A = ITensor(2.0, i, i')
    -A[i => 1, i' => 2] # 2.0, same as: A[i' => 2, i => 1]
    source
    Base.setindex!Method
    setindex!(T::ITensor, x::Number, ivs...)
    +A[i => 1, i' => 2] # 2.0, same as: A[i' => 2, i => 1]
    source
    Base.setindex!Method
    setindex!(T::ITensor, x::Number, ivs...)
     
     setindex!(T::ITensor, x::Number, I::Integer...)
     
    @@ -273,41 +273,41 @@
     
     # Some simple slicing is also supported
     A[i => 2, i' => :] = [2.0 3.0]
    -A[2, :] = [2.0 3.0]
    source

    Properties

    NDTensors.indsMethod
    inds(T::ITensor)

    Return the indices of the ITensor as a Tuple.

    source
    NDTensors.indMethod
    ind(T::ITensor, i::Int)

    Get the Index of the ITensor along dimension i.

    source
    ITensors.dirMethod
    dir(A::ITensor, i::Index)

    Return the direction of the Index i in the ITensor A.

    source

    Priming and tagging

    ITensors.primeMethod
    prime[!](A::ITensor, plinc::Int = 1; <keyword arguments>) -> ITensor
    +A[2, :] = [2.0 3.0]
    source

    Properties

    NDTensors.indsMethod
    inds(T::ITensor)

    Return the indices of the ITensor as a Tuple.

    source
    NDTensors.indMethod
    ind(T::ITensor, i::Int)

    Get the Index of the ITensor along dimension i.

    source
    ITensors.dirMethod
    dir(A::ITensor, i::Index)

    Return the direction of the Index i in the ITensor A.

    source

    Priming and tagging

    ITensors.primeMethod
    prime[!](A::ITensor, plinc::Int = 1; <keyword arguments>) -> ITensor
     
    -prime(inds, plinc::Int = 1; <keyword arguments>) -> IndexSet

    Increase the prime level of the indices of an ITensor or collection of indices.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.setprimeMethod
    setprime[!](A::ITensor, plev::Int; <keyword arguments>) -> ITensor
    +prime(inds, plinc::Int = 1; <keyword arguments>) -> IndexSet

    Increase the prime level of the indices of an ITensor or collection of indices.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.setprimeMethod
    setprime[!](A::ITensor, plev::Int; <keyword arguments>) -> ITensor
     
    -setprime(inds, plev::Int; <keyword arguments>) -> IndexSet

    Set the prime level of the indices of an ITensor or collection of indices.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.noprimeMethod
    noprime[!](A::ITensor; <keyword arguments>) -> ITensor
    +setprime(inds, plev::Int; <keyword arguments>) -> IndexSet

    Set the prime level of the indices of an ITensor or collection of indices.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.noprimeMethod
    noprime[!](A::ITensor; <keyword arguments>) -> ITensor
     
    -noprime(inds; <keyword arguments>) -> IndexSet

    Set the prime level of the indices of an ITensor or collection of indices to zero.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.mapprimeMethod
    replaceprime[!](A::ITensor, plold::Int, plnew::Int; <keyword arguments>) -> ITensor
    +noprime(inds; <keyword arguments>) -> IndexSet

    Set the prime level of the indices of an ITensor or collection of indices to zero.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.mapprimeMethod
    replaceprime[!](A::ITensor, plold::Int, plnew::Int; <keyword arguments>) -> ITensor
     replaceprime[!](A::ITensor, plold => plnew; <keyword arguments>) -> ITensor
     mapprime[!](A::ITensor, <arguments>; <keyword arguments>) -> ITensor
     
     replaceprime(inds, plold::Int, plnew::Int; <keyword arguments>)
     replaceprime(inds::IndexSet, plold => plnew; <keyword arguments>)
    -mapprime(inds, <arguments>; <keyword arguments>)

    Set the prime level of the indices of an ITensor or collection of indices with prime level plold to plnew.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.swapprimeMethod
    swapprime[!](A::ITensor, pl1::Int, pl2::Int; <keyword arguments>) -> ITensor
    +mapprime(inds, <arguments>; <keyword arguments>)

    Set the prime level of the indices of an ITensor or collection of indices with prime level plold to plnew.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.swapprimeMethod
    swapprime[!](A::ITensor, pl1::Int, pl2::Int; <keyword arguments>) -> ITensor
     swapprime[!](A::ITensor, pl1 => pl2; <keyword arguments>) -> ITensor
     
     swapprime(inds, pl1::Int, pl2::Int; <keyword arguments>)
    -swapprime(inds, pl1 => pl2; <keyword arguments>)

    Set the prime level of the indices of an ITensor or collection of indices with prime level pl1 to pl2, and those with prime level pl2 to pl1.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.TagSets.addtagsMethod
    addtags[!](A::ITensor, ts::String; <keyword arguments>) -> ITensor
    +swapprime(inds, pl1 => pl2; <keyword arguments>)

    Set the prime level of the indices of an ITensor or collection of indices with prime level pl1 to pl2, and those with prime level pl2 to pl1.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.TagSets.addtagsMethod
    addtags[!](A::ITensor, ts::String; <keyword arguments>) -> ITensor
     
    -addtags(inds, ts::String; <keyword arguments>)

    Add the tags ts to the indices of an ITensor or collection of indices.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.TagSets.removetagsMethod
    removetags[!](A::ITensor, ts::String; <keyword arguments>) -> ITensor
    +addtags(inds, ts::String; <keyword arguments>)

    Add the tags ts to the indices of an ITensor or collection of indices.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.TagSets.removetagsMethod
    removetags[!](A::ITensor, ts::String; <keyword arguments>) -> ITensor
     
    -removetags(inds, ts::String; <keyword arguments>)

    Remove the tags ts from the indices of an ITensor or collection of indices.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.TagSets.replacetagsMethod
    replacetags[!](A::ITensor, tsold::String, tsnew::String; <keyword arguments>) -> ITensor
    +removetags(inds, ts::String; <keyword arguments>)

    Remove the tags ts from the indices of an ITensor or collection of indices.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.TagSets.replacetagsMethod
    replacetags[!](A::ITensor, tsold::String, tsnew::String; <keyword arguments>) -> ITensor
     
    -replacetags(is::IndexSet, tsold::String, tsnew::String; <keyword arguments>) -> IndexSet

    Replace the tags tsold with tsnew for the indices of an ITensor.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.settagsMethod
    settags[!](A::ITensor, ts::String; <keyword arguments>) -> ITensor
    +replacetags(is::IndexSet, tsold::String, tsnew::String; <keyword arguments>) -> IndexSet

    Replace the tags tsold with tsnew for the indices of an ITensor.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.settagsMethod
    settags[!](A::ITensor, ts::String; <keyword arguments>) -> ITensor
     
    -settags(is::IndexSet, ts::String; <keyword arguments>) -> IndexSet

    Set the tags of the indices of an ITensor or IndexSet to ts.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.swaptagsMethod
    swaptags[!](A::ITensor, ts1::String, ts2::String; <keyword arguments>) -> ITensor
    +settags(is::IndexSet, ts::String; <keyword arguments>) -> IndexSet

    Set the tags of the indices of an ITensor or IndexSet to ts.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source
    ITensors.swaptagsMethod
    swaptags[!](A::ITensor, ts1::String, ts2::String; <keyword arguments>) -> ITensor
     
    -swaptags(is::IndexSet, ts1::String, ts2::String; <keyword arguments>) -> IndexSet

    Swap the tags ts1 with ts2 for the indices of an ITensor.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source

    Index collections set operations

    ITensors.commonindsFunction
    commoninds(A, B; kwargs...)

    Return a Vector with indices that are common between the indices of A and B (the set intersection, similar to Base.intersect).

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.commonindFunction
    commonind(A, B; kwargs...)

    Return the first Index common between the indices of A and B.

    See also commoninds.

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.uniqueindsFunction
    uniqueinds(A, B; kwargs...)

    Return Vector with indices that are unique to the set of indices of A and not in B (the set difference, similar to Base.setdiff).

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.uniqueindFunction
    uniqueind(A, B; kwargs...)

    Return the first Index unique to the set of indices of A and not in B.

    See also uniqueinds.

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.noncommonindsFunction
    noncommoninds(A, B; kwargs...)

    Return a Vector with indices that are not common between the indices of A and B (the symmetric set difference, similar to Base.symdiff).

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.noncommonindFunction
    noncommonind(A, B; kwargs...)

    Return the first Index not common between the indices of A and B.

    See also noncommoninds.

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.unionindsFunction
    unioninds(A, B; kwargs...)

    Return a Vector with indices that are the union of the indices of A and B (the set union, similar to Base.union).

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.unionindFunction
    unionind(A, B; kwargs...)

    Return the first Index in the union of the indices of A and B.

    See also unioninds.

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.hascommonindsFunction
    hascommoninds(A, B; kwargs...)
    +swaptags(is::IndexSet, ts1::String, ts2::String; <keyword arguments>) -> IndexSet

    Swap the tags ts1 with ts2 for the indices of an ITensor.

    Optionally, only modify the indices with the specified keyword arguments.

    Arguments

    • tags = nothing: if specified, only modify Index i if hastags(i, tags) == true.
    • plev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.

    The ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).

    source

    Index collections set operations

    ITensors.commonindsFunction
    commoninds(A, B; kwargs...)

    Return a Vector with indices that are common between the indices of A and B (the set intersection, similar to Base.intersect).

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.commonindFunction
    commonind(A, B; kwargs...)

    Return the first Index common between the indices of A and B.

    See also commoninds.

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.uniqueindsFunction
    uniqueinds(A, B; kwargs...)

    Return Vector with indices that are unique to the set of indices of A and not in B (the set difference, similar to Base.setdiff).

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.uniqueindFunction
    uniqueind(A, B; kwargs...)

    Return the first Index unique to the set of indices of A and not in B.

    See also uniqueinds.

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.noncommonindsFunction
    noncommoninds(A, B; kwargs...)

    Return a Vector with indices that are not common between the indices of A and B (the symmetric set difference, similar to Base.symdiff).

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.noncommonindFunction
    noncommonind(A, B; kwargs...)

    Return the first Index not common between the indices of A and B.

    See also noncommoninds.

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.unionindsFunction
    unioninds(A, B; kwargs...)

    Return a Vector with indices that are the union of the indices of A and B (the set union, similar to Base.union).

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.unionindFunction
    unionind(A, B; kwargs...)

    Return the first Index in the union of the indices of A and B.

    See also unioninds.

    Optional keyword arguments:

    • tags::String - a tag name or comma separated list of tag names that the returned indices must all have
    • plev::Int - common prime level that the returned indices must all have
    • inds - Index or collection of indices. Returned indices must come from this set of indices.
    source
    ITensors.hascommonindsFunction
    hascommoninds(A, B; kwargs...)
     
    -hascommoninds(B; kwargs...) -> f::Function

    Check if the ITensors or sets of indices A and B have common indices.

    If only one ITensor or set of indices B is passed, return a function f such that f(A) = hascommoninds(A, B; kwargs...)

    source

    Index Manipulations

    ITensors.replaceindMethod
    replaceind[!](A::ITensor, i1::Index, i2::Index) -> ITensor

    Replace the Index i1 with the Index i2 in the ITensor.

    The indices must have the same space (i.e. the same dimension and QNs, if applicable).

    source
    ITensors.replaceindsMethod
    replaceinds(A::ITensor, inds1, inds2) -> ITensor
    +hascommoninds(B; kwargs...) -> f::Function

    Check if the ITensors or sets of indices A and B have common indices.

    If only one ITensor or set of indices B is passed, return a function f such that f(A) = hascommoninds(A, B; kwargs...)

    source

    Index Manipulations

    ITensors.replaceindMethod
    replaceind[!](A::ITensor, i1::Index, i2::Index) -> ITensor

    Replace the Index i1 with the Index i2 in the ITensor.

    The indices must have the same space (i.e. the same dimension and QNs, if applicable).

    source
    ITensors.replaceindsMethod
    replaceinds(A::ITensor, inds1, inds2) -> ITensor
     
    -replaceinds!(A::ITensor, inds1, inds2)

    Replace the Index inds1[n] with the Index inds2[n] in the ITensor, where n runs from 1 to length(inds1) == length(inds2).

    The indices must have the same space (i.e. the same dimension and QNs, if applicable).

    The storage of the ITensor is not modified or copied (the output ITensor is a view of the input ITensor).

    source
    ITensors.swapindMethod
    swapind(A::ITensor, i1::Index, i2::Index) -> ITensor
    +replaceinds!(A::ITensor, inds1, inds2)

    Replace the Index inds1[n] with the Index inds2[n] in the ITensor, where n runs from 1 to length(inds1) == length(inds2).

    The indices must have the same space (i.e. the same dimension and QNs, if applicable).

    The storage of the ITensor is not modified or copied (the output ITensor is a view of the input ITensor).

    source
    ITensors.swapindMethod
    swapind(A::ITensor, i1::Index, i2::Index) -> ITensor
     
    -swapind!(A::ITensor, i1::Index, i2::Index)

    Swap the Index i1 with the Index i2 in the ITensor.

    The indices must have the same space (i.e. the same dimension and QNs, if applicable).

    source
    ITensors.swapindsMethod
    swapinds(A::ITensor, inds1, inds2) -> ITensor
    +swapind!(A::ITensor, i1::Index, i2::Index)

    Swap the Index i1 with the Index i2 in the ITensor.

    The indices must have the same space (i.e. the same dimension and QNs, if applicable).

    source
    ITensors.swapindsMethod
    swapinds(A::ITensor, inds1, inds2) -> ITensor
     
    -swapinds!(A::ITensor, inds1, inds2)

    Swap the Index inds1[n] with the Index inds2[n] in the ITensor, where n runs from 1 to length(inds1) == length(inds2).

    The indices must have the same space (i.e. the same dimension and QNs, if applicable).

    The storage of the ITensor is not modified or copied (the output ITensor is a view of the input ITensor).

    source

    Math operations

    Base.:*Method
    A::ITensor * B::ITensor
    +swapinds!(A::ITensor, inds1, inds2)

    Swap the Index inds1[n] with the Index inds2[n] in the ITensor, where n runs from 1 to length(inds1) == length(inds2).

    The indices must have the same space (i.e. the same dimension and QNs, if applicable).

    The storage of the ITensor is not modified or copied (the output ITensor is a view of the input ITensor).

    source

    Math operations

    Base.:*Method
    A::ITensor * B::ITensor
     contract(A::ITensor, B::ITensor)

    Contract ITensors A and B to obtain a new ITensor. This contraction * operator finds all matching indices common to A and B and sums over them, such that the result will have only the unique indices of A and B. To prevent indices from matching, their prime level or tags can be modified such that they no longer compare equal - for more information see the documentation on Index objects.

    Examples

    i = Index(2,"index_i"); j = Index(4,"index_j"); k = Index(3,"index_k")
     
     A = randomITensor(i,j)
    @@ -324,7 +324,7 @@
     
     A = randomITensor(i,j,k)
     B = randomITensor(k,i,j)
    -C = A * B # inner product of A and B, all indices contracted
    source
    ITensors.dagMethod
    dag(T::ITensor; allow_alias = true)

    Complex conjugate the elements of the ITensor T and dagger the indices.

    By default, an alias of the ITensor is returned (i.e. the output ITensor may share data with the input ITensor). If allow_alias = false, an alias is never returned.

    source
    ITensors.directsumMethod
    directsum(A::Pair{ITensor}, B::Pair{ITensor}, ...; tags)
    +C = A * B # inner product of A and B, all indices contracted
    source
    ITensors.dagMethod
    dag(T::ITensor; allow_alias = true)

    Complex conjugate the elements of the ITensor T and dagger the indices.

    By default, an alias of the ITensor is returned (i.e. the output ITensor may share data with the input ITensor). If allow_alias = false, an alias is never returned.

    source
    ITensors.directsumMethod
    directsum(A::Pair{ITensor}, B::Pair{ITensor}, ...; tags)
     
     directsum(output_inds, A::Pair{ITensor}, B::Pair{ITensor}, ...; tags)

    Given a list of pairs of ITensors and indices, perform a partial direct sum of the tensors over the specified indices. Indices that are not specified to be summed must match between the tensors.

    (Note: Pair{ITensor} in Julia is short for Pair{ITensor,<:Any} which means any pair T => x where T is an ITensor.)

    If all indices are specified then the operation is equivalent to creating a block diagonal tensor.

    Returns the ITensor representing the partial direct sum as well as the new direct summed indices. The tags of the direct summed indices are specified by the keyword arguments.

    Optionally, pass the new direct summed indices of the output tensor as the first argument (either a single Index or a collection), which must be proper direct sums of the input indices that are specified to be direct summed.

    See Section 2.3 of https://arxiv.org/abs/1405.7786 for a definition of a partial direct sum of tensors.

    Examples

    x = Index(2, "x")
     i1 = Index(3, "i1")
    @@ -350,18 +350,18 @@
     S, s = directsum(A1 => (i1, j1), A2 => (i2, j2); tags = ["sum_i", "sum_j"])
     length(s) == 2
     dim(s[1]) == dim(i1) + dim(i2)
    -dim(s[2]) == dim(j1) + dim(j2)
    source
    Base.expMethod
    exp(A::ITensor, Linds=Rinds', Rinds=inds(A,plev=0); ishermitian = false)

    Compute the exponential of the tensor A by treating it as a matrix $A_{lr}$ with the left index l running over all indices in Linds and r running over all indices in Rinds.

    Only accepts index lists Linds,Rinds such that: (1) length(Linds) + length(Rinds) == length(inds(A)) (2) length(Linds) == length(Rinds) (3) For each pair of indices (Linds[n],Rinds[n]), Linds[n] and Rinds[n] represent the same Hilbert space (the same QN structure in the QN case, or just the same length in the dense case), and appear in A with opposite directions.

    When ishermitian=true the exponential of Hermitian(A_{lr}) is computed internally.

    source
    LinearAlgebra.nullspaceMethod
    nullspace(T::ITensor, left_inds...; tags="n", atol=1E-12, kwargs...)

    Viewing the ITensor T as a matrix with the provided left_inds viewed as the row space and remaining indices viewed as the right indices or column space, the nullspace function computes the right null space. That is, it will return a tensor N acting on the right indices of T such that T*N is zero. The returned tensor N will also have a new index with the label "n" which indexes through the 'vectors' in the null space.

    For example, if T has the indices i,j,k, calling N = nullspace(T,i,k) returns N with index j such that

           ___       ___
    +dim(s[2]) == dim(j1) + dim(j2)
    source
    Base.expMethod
    exp(A::ITensor, Linds=Rinds', Rinds=inds(A,plev=0); ishermitian = false)

    Compute the exponential of the tensor A by treating it as a matrix $A_{lr}$ with the left index l running over all indices in Linds and r running over all indices in Rinds.

    Only accepts index lists Linds,Rinds such that: (1) length(Linds) + length(Rinds) == length(inds(A)) (2) length(Linds) == length(Rinds) (3) For each pair of indices (Linds[n],Rinds[n]), Linds[n] and Rinds[n] represent the same Hilbert space (the same QN structure in the QN case, or just the same length in the dense case), and appear in A with opposite directions.

    When ishermitian=true the exponential of Hermitian(A_{lr}) is computed internally.

    source
    LinearAlgebra.nullspaceMethod
    nullspace(T::ITensor, left_inds...; tags="n", atol=1E-12, kwargs...)

    Viewing the ITensor T as a matrix with the provided left_inds viewed as the row space and remaining indices viewed as the right indices or column space, the nullspace function computes the right null space. That is, it will return a tensor N acting on the right indices of T such that T*N is zero. The returned tensor N will also have a new index with the label "n" which indexes through the 'vectors' in the null space.

    For example, if T has the indices i,j,k, calling N = nullspace(T,i,k) returns N with index j such that

           ___       ___
       i --|   |     |   |
           | T |--j--| N |--n  ≈ 0
       k --|   |     |   |
    -       ---       ---

    The index n can be obtained by calling n = uniqueindex(N,T)

    Note that the implementation of this function is subject to change in the future, in which case the precise atol value that gives a certain null space size may change in future versions of ITensor.

    Keyword arguments:

    • atol::Float64=1E-12 - singular values of T†*T below this value define the null space
    • tags::String="n" - choose the tags of the index selecting elements of the null space
    source

    Decompositions

    LinearAlgebra.svdMethod
    svd(A::ITensor, inds::Index...; <keyword arguments>)

    Singular value decomposition (SVD) of an ITensor A, computed by treating the "left indices" provided collectively as a row index, and the remaining "right indices" as a column index (matricization of a tensor).

    The first three return arguments are U, S, and V, such that A ≈ U * S * V.

    Whether or not the SVD performs a trunction depends on the keyword arguments provided.

    If the left or right set of indices are empty, all input indices are put on V or U respectively. To specify an empty set of left indices, you must explicitly use svd(A, ()) (svd(A) is currently undefined).

    Examples

    Computing the SVD of an order-three ITensor, such that the indices i and k end up on U and j ends up on V

    i = Index(2)
    +       ---       ---

    The index n can be obtained by calling n = uniqueindex(N,T)

    Note that the implementation of this function is subject to change in the future, in which case the precise atol value that gives a certain null space size may change in future versions of ITensor.

    Keyword arguments:

    • atol::Float64=1E-12 - singular values of T†*T below this value define the null space
    • tags::String="n" - choose the tags of the index selecting elements of the null space
    source

    Decompositions

    LinearAlgebra.svdMethod
    svd(A::ITensor, inds::Index...; <keyword arguments>)

    Singular value decomposition (SVD) of an ITensor A, computed by treating the "left indices" provided collectively as a row index, and the remaining "right indices" as a column index (matricization of a tensor).

    The first three return arguments are U, S, and V, such that A ≈ U * S * V.

    Whether or not the SVD performs a trunction depends on the keyword arguments provided.

    If the left or right set of indices are empty, all input indices are put on V or U respectively. To specify an empty set of left indices, you must explicitly use svd(A, ()) (svd(A) is currently undefined).

    Examples

    Computing the SVD of an order-three ITensor, such that the indices i and k end up on U and j ends up on V

    i = Index(2)
     j = Index(5)
     k = Index(2)
     A = randomITensor(i, j, k)
     U, S, V = svd(A, i, k);
     @show norm(A - U * S * V) <= 10 * eps() * norm(A)

    The following code will truncate the last 2 singular values, since the total number of singular values is 4. The norm of the difference with the original tensor will be the sqrt root of the sum of the squares of the singular values that get truncated.

    trunc, Strunc, Vtrunc = svd(A, i, k; maxdim=2);
     @show norm(A - Utrunc * Strunc * Vtrunc) ≈ sqrt(S[3, 3]^2 + S[4, 4]^2)

    Alternatively we can specify that we want to truncate the weights of the singular values up to a certain cutoff, so the total error will be no larger than the cutoff.

    Utrunc2, Strunc2, Vtrunc2 = svd(A, i, k; cutoff=1e-10);
    -@show norm(A - Utrunc2 * Strunc2 * Vtrunc2) <= 1e-10

    Keywords

    • maxdim::Int: the maximum number of singular values to keep.
    • mindim::Int: the minimum number of singular values to keep.
    • cutoff::Float64: set the desired truncation error of the SVD, by default defined as the sum of the squares of the smallest singular values.
    • lefttags::String = "Link,u": set the tags of the Index shared by U and S.
    • righttags::String = "Link,v": set the tags of the Index shared by S and V.
    • alg::String = "divide_and_conquer". Options:
    • "divide_and_conquer" - A divide-and-conquer algorithm. LAPACK's gesdd. Fast, but may lead to some innacurate singular values for very ill-conditioned matrices. Also may sometimes fail to converge, leading to errors (in which case "qr_iteration" or "recursive" can be tried).
      • "qr_iteration" - Typically slower but more accurate for very ill-conditioned matrices compared to "divide_and_conquer". LAPACK's gesvd.
      • "recursive" - ITensor's custom svd. Very reliable, but may be slow if high precision is needed. To get an svd of a matrix A, an eigendecomposition of $A^{\dagger} A$ is used to compute U and then a qr of $A^{\dagger} U$ is used to compute V. This is performed recursively to compute small singular values.
    • use_absolute_cutoff::Bool = false: set if all probability weights below the cutoff value should be discarded, rather than the sum of discarded weights.
    • use_relative_cutoff::Bool = true: set if the singular values should be normalized for the sake of truncation.
    • min_blockdim::Int = 0: for SVD of block-sparse or QN ITensors, require that the number of singular values kept be greater than or equal to this value when possible

    See also: factorize, eigen

    source
    LinearAlgebra.eigenMethod
    eigen(A::ITensor[, Linds, Rinds]; <keyword arguments>)

    Eigendecomposition of an ITensor A, computed by treating the "left indices" Linds provided collectively as a row index, and remaining "right indices" Rinds as a column index (matricization of a tensor).

    If no indices are provided, pairs of primed and unprimed indices are searched for, with Linds taken to be the primed indices and Rinds taken to be the unprimed indices.

    The return arguments are the eigenvalues D and eigenvectors U as tensors, such that A * U ∼ U * D (more precisely they are approximately equal up to proper replacements of indices, see the example for details).

    Whether or not eigen performs a trunction depends on the keyword arguments provided. Note that truncation is only well defined for positive semidefinite matrices.

    Arguments

    - `maxdim::Int`: the maximum number of singular values to keep.
    +@show norm(A - Utrunc2 * Strunc2 * Vtrunc2) <= 1e-10

    Keywords

    • maxdim::Int: the maximum number of singular values to keep.
    • mindim::Int: the minimum number of singular values to keep.
    • cutoff::Float64: set the desired truncation error of the SVD, by default defined as the sum of the squares of the smallest singular values.
    • lefttags::String = "Link,u": set the tags of the Index shared by U and S.
    • righttags::String = "Link,v": set the tags of the Index shared by S and V.
    • alg::String = "divide_and_conquer". Options:
    • "divide_and_conquer" - A divide-and-conquer algorithm. LAPACK's gesdd. Fast, but may lead to some innacurate singular values for very ill-conditioned matrices. Also may sometimes fail to converge, leading to errors (in which case "qr_iteration" or "recursive" can be tried).
      • "qr_iteration" - Typically slower but more accurate for very ill-conditioned matrices compared to "divide_and_conquer". LAPACK's gesvd.
      • "recursive" - ITensor's custom svd. Very reliable, but may be slow if high precision is needed. To get an svd of a matrix A, an eigendecomposition of $A^{\dagger} A$ is used to compute U and then a qr of $A^{\dagger} U$ is used to compute V. This is performed recursively to compute small singular values.
    • use_absolute_cutoff::Bool = false: set if all probability weights below the cutoff value should be discarded, rather than the sum of discarded weights.
    • use_relative_cutoff::Bool = true: set if the singular values should be normalized for the sake of truncation.
    • min_blockdim::Int = 0: for SVD of block-sparse or QN ITensors, require that the number of singular values kept be greater than or equal to this value when possible

    See also: factorize, eigen

    source
    LinearAlgebra.eigenMethod
    eigen(A::ITensor[, Linds, Rinds]; <keyword arguments>)

    Eigendecomposition of an ITensor A, computed by treating the "left indices" Linds provided collectively as a row index, and remaining "right indices" Rinds as a column index (matricization of a tensor).

    If no indices are provided, pairs of primed and unprimed indices are searched for, with Linds taken to be the primed indices and Rinds taken to be the unprimed indices.

    The return arguments are the eigenvalues D and eigenvectors U as tensors, such that A * U ∼ U * D (more precisely they are approximately equal up to proper replacements of indices, see the example for details).

    Whether or not eigen performs a trunction depends on the keyword arguments provided. Note that truncation is only well defined for positive semidefinite matrices.

    Arguments

    - `maxdim::Int`: the maximum number of singular values to keep.
     - `mindim::Int`: the minimum number of singular values to keep.
     - `cutoff::Float64`: set the desired truncation error of the eigenvalues,
        by default defined as the sum of the squares of the smallest eigenvalues.
    @@ -393,7 +393,7 @@
     D, U = eigen(A, Linds, Rinds)
     dl, dr = uniqueind(D, U), commonind(D, U)
     Ul = replaceinds(U, (Rinds..., dr) => (Linds..., dl))
    -A * U ≈ Ul * D # true

    See also: svd, factorize

    source
    LinearAlgebra.factorizeMethod
    factorize(A::ITensor, Linds::Index...; <keyword arguments>)

    Perform a factorization of A into ITensors L and R such that A ≈ L * R.

    Arguments

    • ortho::String = "left": Choose orthogonality properties of the factorization.
      • "left": the left factor L is an orthogonal basis such that L * dag(prime(L, commonind(L,R))) ≈ I.
      • "right": the right factor R forms an orthogonal basis.
      • "none", neither of the factors form an orthogonal basis, and in general are made as symmetrically as possible (depending on the decomposition used).
    • which_decomp::Union{String, Nothing} = nothing: choose what kind of decomposition is used.
      • nothing: choose the decomposition automatically based on the other arguments. For example, when nothing is chosen and ortho = "left" or "right", and a cutoff is provided, svd or eigen is used depending on the provided cutoff (eigen is only used when the cutoff is greater than 1e-12, since it has a lower precision). When no truncation is requested qr is used for dense ITensors and svd for block-sparse ITensors (in the future qr will be used also for block-sparse ITensors in this case).
      • "svd": L = U and R = S * V for ortho = "left", L = U * S and R = V for ortho = "right", and L = U * sqrt.(S) and R = sqrt.(S) * V for ortho = "none". To control which svd algorithm is choose, use the svd_alg keyword argument. See the documentation for svd for the supported algorithms, which are the same as those accepted by the alg keyword argument.
      • "eigen": L = U and $R = U^{\dagger} A$ where U is determined from the eigendecompositon $A A^{\dagger} = U D U^{\dagger}$ for ortho = "left" (and vice versa for ortho = "right"). "eigen" is not supported for ortho = "none".
      • "qr": L=Q and R an upper-triangular matrix when ortho = "left", and R = Q and L a lower-triangular matrix when ortho = "right" (currently supported for dense ITensors only). In the future, other decompositions like QR (for block-sparse ITensors), polar, cholesky, LU, etc. are expected to be supported.

    For truncation arguments, see: svd

    source

    Memory operations

    ITensors.permuteMethod
    permute(T::ITensor, inds...; allow_alias = false)

    Return a new ITensor T with indices permuted according to the input indices inds. The storage of the ITensor is permuted accordingly.

    If called with allow_alias = true, it avoids copying data if possible. Therefore, it may return an alias of the input ITensor (an ITensor that shares the same data), such as if the permutation turns out to be trivial.

    By default, allow_alias = false, and it never returns an alias of the input ITensor.

    Examples

    i = Index(2, "index_i"); j = Index(4, "index_j"); k = Index(3, "index_k");
    +A * U ≈ Ul * D # true

    See also: svd, factorize

    source
    LinearAlgebra.factorizeMethod
    factorize(A::ITensor, Linds::Index...; <keyword arguments>)

    Perform a factorization of A into ITensors L and R such that A ≈ L * R.

    Arguments

    • ortho::String = "left": Choose orthogonality properties of the factorization.
      • "left": the left factor L is an orthogonal basis such that L * dag(prime(L, commonind(L,R))) ≈ I.
      • "right": the right factor R forms an orthogonal basis.
      • "none", neither of the factors form an orthogonal basis, and in general are made as symmetrically as possible (depending on the decomposition used).
    • which_decomp::Union{String, Nothing} = nothing: choose what kind of decomposition is used.
      • nothing: choose the decomposition automatically based on the other arguments. For example, when nothing is chosen and ortho = "left" or "right", and a cutoff is provided, svd or eigen is used depending on the provided cutoff (eigen is only used when the cutoff is greater than 1e-12, since it has a lower precision). When no truncation is requested qr is used for dense ITensors and svd for block-sparse ITensors (in the future qr will be used also for block-sparse ITensors in this case).
      • "svd": L = U and R = S * V for ortho = "left", L = U * S and R = V for ortho = "right", and L = U * sqrt.(S) and R = sqrt.(S) * V for ortho = "none". To control which svd algorithm is choose, use the svd_alg keyword argument. See the documentation for svd for the supported algorithms, which are the same as those accepted by the alg keyword argument.
      • "eigen": L = U and $R = U^{\dagger} A$ where U is determined from the eigendecompositon $A A^{\dagger} = U D U^{\dagger}$ for ortho = "left" (and vice versa for ortho = "right"). "eigen" is not supported for ortho = "none".
      • "qr": L=Q and R an upper-triangular matrix when ortho = "left", and R = Q and L a lower-triangular matrix when ortho = "right" (currently supported for dense ITensors only). In the future, other decompositions like QR (for block-sparse ITensors), polar, cholesky, LU, etc. are expected to be supported.

    For truncation arguments, see: svd

    source

    Memory operations

    ITensors.permuteMethod
    permute(T::ITensor, inds...; allow_alias = false)

    Return a new ITensor T with indices permuted according to the input indices inds. The storage of the ITensor is permuted accordingly.

    If called with allow_alias = true, it avoids copying data if possible. Therefore, it may return an alias of the input ITensor (an ITensor that shares the same data), such as if the permutation turns out to be trivial.

    By default, allow_alias = false, and it never returns an alias of the input ITensor.

    Examples

    i = Index(2, "index_i"); j = Index(4, "index_j"); k = Index(3, "index_k");
     T = randomITensor(i, j, k)
     
     pT_1 = permute(T, k, i, j)
    @@ -409,4 +409,4 @@
     
     pT_alias = permute(T, i, j, k; allow_alias = true)
     pT_alias[1, 1, 1] = 12
    -T[1, 1, 1] == pT_alias[1, 1, 1]
    source
    NDTensors.denseMethod
    dense(T::ITensor)

    Make a new ITensor where the storage is the closest Dense storage, avoiding allocating new data if possible. For example, an ITensor with Diag storage will become Dense storage, filled with zeros except for the diagonal values.

    source
    NDTensors.denseblocksMethod
    denseblocks(T::ITensor)

    Make a new ITensor where any blocks which have a sparse format, such as diagonal sparsity, are made dense while still preserving the outer block-sparse structure. This method avoids allocating new data if possible.

    For example, an ITensor with DiagBlockSparse storage will have BlockSparse storage afterwards.

    source
    +T[1, 1, 1] == pT_alias[1, 1, 1]source
    NDTensors.denseMethod
    dense(T::ITensor)

    Make a new ITensor where the storage is the closest Dense storage, avoiding allocating new data if possible. For example, an ITensor with Diag storage will become Dense storage, filled with zeros except for the diagonal values.

    source
    NDTensors.denseblocksMethod
    denseblocks(T::ITensor)

    Make a new ITensor where any blocks which have a sparse format, such as diagonal sparsity, are made dense while still preserving the outer block-sparse structure. This method avoids allocating new data if possible.

    For example, an ITensor with DiagBlockSparse storage will have BlockSparse storage afterwards.

    source
    diff --git a/previews/PR1410/IncludedSiteTypes.html b/previews/PR1410/IncludedSiteTypes.html index 8a82cf9229..5c9d80ad80 100644 --- a/previews/PR1410/IncludedSiteTypes.html +++ b/previews/PR1410/IncludedSiteTypes.html @@ -13,4 +13,4 @@ sites = siteinds("Electron",N)

    Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:

    For example:

    sites = siteinds("Electron",N; conserve_nfparity=true)

    "Electron" States

    The available state names for "Electron" sites are:

    "Electron" Operators

    Operators associated with "Electron" sites can be made using the op function, for example

    Cup = op("Cup",s)
     Cup4 = op("Cup",sites[4])

    Single-fermion operators:

    Non-fermionic single particle operators (these do not have Jordan-Wigner string attached, so will commute within systems such as OpSum or the apply function):

    "tJ" SiteType

    "tJ" sites are similar to electron sites, but cannot be doubly occupied The states of site indices with the "tJ" SiteType correspond to $|0\rangle$, $|\!\uparrow\rangle$, $|\!\downarrow\rangle$.

    Making a single "tJ" site or collection of N "tJ" sites

    s = siteind("tJ")
     sites = siteinds("tJ",N)

    Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:

    For example:

    sites = siteinds("tJ",N; conserve_nfparity=true)

    "tJ" States

    The available state names for "tJ" sites are:

    "tJ" Operators

    Operators associated with "tJ" sites can be made using the op function, for example

    Cup = op("Cup",s)
    -Cup4 = op("Cup",sites[4])

    Single-fermion operators:

    Non-fermionic single particle operators (these do not have Jordan-Wigner string attached, so will commute within systems such as OpSum or the apply function):

    +Cup4 = op("Cup",sites[4])

    Single-fermion operators:

    Non-fermionic single particle operators (these do not have Jordan-Wigner string attached, so will commute within systems such as OpSum or the apply function):

    diff --git a/previews/PR1410/IndexSetType.html b/previews/PR1410/IndexSetType.html index a372cd9384..e5a6a3578c 100644 --- a/previews/PR1410/IndexSetType.html +++ b/previews/PR1410/IndexSetType.html @@ -1,9 +1,9 @@ -Index collections · ITensors.jl

    Index collections

    Collections of Index are used throughout ITensors.jl to represent the dimensions of tensors. In general, collections that are recognized and returned by ITensors.jl functions are either Vector of Index or Tuple of Index, depending on the context. For example internally an ITensor has a static number of indices so stores a Tuple of Index, while set operations like commoninds((i, j, k), (j, k, l)) will return a Vector [j, k] since the operation is inherently dynamic, i.e. the number of indices in the intersection can't in general be known before running the code. Vector of Index and Tuple of Index can usually be used interchangeably, but one or the other may be faster depending on the operation being performed.

    Priming and tagging

    Documentation for priming and tagging collections of Index can be found in the ITensor Priming and tagging section.

    Set operations

    Documentation for set operations involving Index collections can be found in the ITensor Index collections set operations section.

    Subsets

    ITensors.getfirstMethod
    getfirst(f::Function, is::Indices)

    Get the first Index matching the pattern function, return nothing if not found.

    source
    ITensors.getfirstMethod
    getfirst(is::Indices)

    Return the first Index in the Indices. If the Indices is empty, return nothing.

    source

    Iterating

    ITensors.eachvalMethod
    eachval(is::Index...)
    -eachval(is::Tuple{Vararg{Index}})

    Create an iterator whose values correspond to a Cartesian indexing over the dimensions of the provided Index objects.

    source
    ITensors.eachindvalMethod
    eachindval(is::Index...)
    +Index collections · ITensors.jl

    Index collections

    Collections of Index are used throughout ITensors.jl to represent the dimensions of tensors. In general, collections that are recognized and returned by ITensors.jl functions are either Vector of Index or Tuple of Index, depending on the context. For example internally an ITensor has a static number of indices so stores a Tuple of Index, while set operations like commoninds((i, j, k), (j, k, l)) will return a Vector [j, k] since the operation is inherently dynamic, i.e. the number of indices in the intersection can't in general be known before running the code. Vector of Index and Tuple of Index can usually be used interchangeably, but one or the other may be faster depending on the operation being performed.

    Priming and tagging

    Documentation for priming and tagging collections of Index can be found in the ITensor Priming and tagging section.

    Set operations

    Documentation for set operations involving Index collections can be found in the ITensor Index collections set operations section.

    Subsets

    ITensors.getfirstMethod
    getfirst(f::Function, is::Indices)

    Get the first Index matching the pattern function, return nothing if not found.

    source
    ITensors.getfirstMethod
    getfirst(is::Indices)

    Return the first Index in the Indices. If the Indices is empty, return nothing.

    source

    Iterating

    ITensors.eachvalMethod
    eachval(is::Index...)
    +eachval(is::Tuple{Vararg{Index}})

    Create an iterator whose values correspond to a Cartesian indexing over the dimensions of the provided Index objects.

    source
    ITensors.eachindvalMethod
    eachindval(is::Index...)
     eachindval(is::Tuple{Vararg{Index}})

    Create an iterator whose values are Index=>value pairs corresponding to a Cartesian indexing over the dimensions of the provided Index objects.

    Example

    i = Index(3; tags="i")
     j = Index(2; tags="j")
     T = randomITensor(j, i)
     for iv in eachindval(i, j)
       @show T[iv...]
    -end
    source
    ITensors.dirMethod
    dir(is::Indices, i::Index)

    Return the direction of the Index i in the Indices is.

    source
    +end
    source
    ITensors.dirMethod
    dir(is::Indices, i::Index)

    Return the direction of the Index i in the Indices is.

    source
    diff --git a/previews/PR1410/IndexType.html b/previews/PR1410/IndexType.html index a4d2baaacf..ee9829d5e0 100644 --- a/previews/PR1410/IndexType.html +++ b/previews/PR1410/IndexType.html @@ -1,5 +1,5 @@ -Index · ITensors.jl

    Index

    Description

    ITensors.IndexType

    An Index represents a single tensor index with fixed dimension dim. Copies of an Index compare equal unless their tags are different.

    An Index carries a TagSet, a set of tags which are small strings that specify properties of the Index to help distinguish it from other Indices. There is a special tag which is referred to as the integer tag or prime level which can be incremented or decremented with special priming functions.

    Internally, an Index has a fixed id number, which is how the ITensor library knows two indices are copies of a single original Index. Index objects must have the same id, as well as the tags to compare equal.

    source
    ITensors.QNIndexType

    A QN Index is an Index with QN block storage instead of just an integer dimension. The QN block storage is a vector of pairs of QNs and block dimensions. The total dimension of a QN Index is the sum of the dimensions of the blocks of the Index.

    source

    Constructors

    ITensors.IndexMethod
    Index(dim::Int; tags::Union{AbstractString, TagSet} = "",
    +Index · ITensors.jl

    Index

    Description

    ITensors.IndexType

    An Index represents a single tensor index with fixed dimension dim. Copies of an Index compare equal unless their tags are different.

    An Index carries a TagSet, a set of tags which are small strings that specify properties of the Index to help distinguish it from other Indices. There is a special tag which is referred to as the integer tag or prime level which can be incremented or decremented with special priming functions.

    Internally, an Index has a fixed id number, which is how the ITensor library knows two indices are copies of a single original Index. Index objects must have the same id, as well as the tags to compare equal.

    source
    ITensors.QNIndexType

    A QN Index is an Index with QN block storage instead of just an integer dimension. The QN block storage is a vector of pairs of QNs and block dimensions. The total dimension of a QN Index is the sum of the dimensions of the blocks of the Index.

    source

    Constructors

    ITensors.IndexMethod
    Index(dim::Int; tags::Union{AbstractString, TagSet} = "",
                     plev::Int = 0)

    Create an Index with a unique id, a TagSet given by tags, and a prime level plev.

    Examples

    julia> i = Index(2; tags="l", plev=1)
     (dim=2|id=818|"l")'
     
    @@ -10,7 +10,7 @@
     1
     
     julia> tags(i)
    -"l"
    source
    ITensors.IndexMethod
    Index(dim::Integer, tags::Union{AbstractString, TagSet}; plev::Int = 0)

    Create an Index with a unique id and a tagset given by tags.

    Examples

    julia> i = Index(2, "l,tag")
    +"l"
    source
    ITensors.IndexMethod
    Index(dim::Integer, tags::Union{AbstractString, TagSet}; plev::Int = 0)

    Create an Index with a unique id and a tagset given by tags.

    Examples

    julia> i = Index(2, "l,tag")
     (dim=2|id=58|"l,tag")
     
     julia> dim(i)
    @@ -20,12 +20,12 @@
     0
     
     julia> tags(i)
    -"l,tag"
    source
    ITensors.IndexMethod
    Index(qnblocks::Pair{QN, Int64}...; dir::Arrow = Out,
                                         tags = "",
    -                                    plev::Integer = 0)

    Construct a QN Index from a list of pairs of QN and block dimensions.

    Example

    Index(QN("Sz", -1) => 1, QN("Sz", 1) => 1; tags = "i")
    source
    ITensors.IndexMethod
    Index(qnblocks::Vector{Pair{QN, Int64}}; dir::Arrow = Out,
    +                                    plev::Integer = 0)

    Construct a QN Index from a list of pairs of QN and block dimensions.

    Example

    Index(QN("Sz", -1) => 1, QN("Sz", 1) => 1; tags = "i")
    source
    ITensors.IndexMethod
    Index(qnblocks::Vector{Pair{QN, Int64}}; dir::Arrow = Out,
                                              tags = "",
    -                                         plev::Integer = 0)

    Construct a QN Index from a Vector of pairs of QN and block dimensions.

    Note: in the future, this may enforce that all blocks have the same QNs (which would allow for some optimizations, for example when constructing random QN ITensors).

    Example

    Index([QN("Sz", -1) => 1, QN("Sz", 1) => 1]; tags = "i")
    source
    ITensors.IndexMethod
    Index(qnblocks::Vector{Pair{QN, Int64}}, tags; dir::Arrow = Out,
    -                                               plev::Integer = 0)

    Construct a QN Index from a Vector of pairs of QN and block dimensions.

    Example

    Index([QN("Sz", -1) => 1, QN("Sz", 1) => 1], "i"; dir = In)
    source

    Properties

    ITensors.idMethod
    id(i::Index)

    Obtain the id of an Index, which is a unique 64 digit integer.

    source
    ITensors.hasidMethod
    hasid(i::Index, id::ITensors.IDType)

    Check if an Index i has the provided id.

    Examples

    julia> i = Index(2)
    +                                         plev::Integer = 0)

    Construct a QN Index from a Vector of pairs of QN and block dimensions.

    Note: in the future, this may enforce that all blocks have the same QNs (which would allow for some optimizations, for example when constructing random QN ITensors).

    Example

    Index([QN("Sz", -1) => 1, QN("Sz", 1) => 1]; tags = "i")
    source
    ITensors.IndexMethod
    Index(qnblocks::Vector{Pair{QN, Int64}}, tags; dir::Arrow = Out,
    +                                               plev::Integer = 0)

    Construct a QN Index from a Vector of pairs of QN and block dimensions.

    Example

    Index([QN("Sz", -1) => 1, QN("Sz", 1) => 1], "i"; dir = In)
    source

    Properties

    ITensors.idMethod
    id(i::Index)

    Obtain the id of an Index, which is a unique 64 digit integer.

    source
    ITensors.hasidMethod
    hasid(i::Index, id::ITensors.IDType)

    Check if an Index i has the provided id.

    Examples

    julia> i = Index(2)
     (dim=2|id=321)
     
     julia> hasid(i, id(i))
    @@ -35,23 +35,23 @@
     (dim=2|id=17)
     
     julia> hasid(i, id(j))
    -false
    source
    ITensors.TagSets.set_strict_tags!Method
    set_strict_tags!(enable::Bool) -> Bool
    -

    Enable or disable checking for overflow of the number of tags of a TagSet or the number of characters of a tag. If enabled (set to true), an error will be thrown if overflow occurs, otherwise the overflow will be ignored and the extra tags or tag characters will be dropped. This could cause unexpected bugs if tags are being used to distinguish Index objects that have the same ids and prime levels, but that is generally discouraged and should only be used if you know what you are doing.

    See also ITensors.using_strict_tags.

    source
    ITensors.TagSets.hastagsMethod
    hastags(i::Index, ts::Union{AbstractString,TagSet})

    Check if an Index i has the provided tags, which can be a string of comma-separated tags or a TagSet object.

    Examples

    julia> i = Index(2, "SpinHalf,Site,n=3")
    +false
    source
    ITensors.TagSets.set_strict_tags!Method
    set_strict_tags!(enable::Bool) -> Bool
    +

    Enable or disable checking for overflow of the number of tags of a TagSet or the number of characters of a tag. If enabled (set to true), an error will be thrown if overflow occurs, otherwise the overflow will be ignored and the extra tags or tag characters will be dropped. This could cause unexpected bugs if tags are being used to distinguish Index objects that have the same ids and prime levels, but that is generally discouraged and should only be used if you know what you are doing.

    See also ITensors.using_strict_tags.

    source
    ITensors.TagSets.hastagsMethod
    hastags(i::Index, ts::Union{AbstractString,TagSet})

    Check if an Index i has the provided tags, which can be a string of comma-separated tags or a TagSet object.

    Examples

    julia> i = Index(2, "SpinHalf,Site,n=3")
     (dim=2|id=861|"Site,SpinHalf,n=3")
     
     julia> hastags(i, "SpinHalf,Site")
     true
     
     julia> hastags(i, "Link")
    -false
    source
    ITensors.hasplevMethod
    hasplev(i::Index, plev::Int)

    Check if an Index i has the provided prime level.

    Examples

    julia> i = Index(2; plev=2)
    +false
    source
    ITensors.hasplevMethod
    hasplev(i::Index, plev::Int)

    Check if an Index i has the provided prime level.

    Examples

    julia> i = Index(2; plev=2)
     (dim=2|id=543)''
     
     julia> hasplev(i, 2)
     true
     
     julia> hasplev(i, 1)
    -false
    source
    NDTensors.dimMethod
    dim(i::Index)

    Obtain the dimension of an Index.

    For a QN Index, this is the sum of the block dimensions.

    source
    Base.:==Method
    ==(i1::Index, i1::Index)

    Compare indices for equality. First the id's are compared, then the prime levels are compared, and finally the tags are compared.

    source
    ITensors.dirMethod
    dir(i::Index)

    Return the direction of an Index (ITensors.In, ITensors.Out, or ITensors.Neither).

    source

    Priming and tagging methods

    ITensors.primeMethod
    prime(i::Index, plinc::Int = 1)

    Return a copy of Index i with its prime level incremented by the amount plinc

    source
    Base.:^Method
    ^(i::Index, pl::Int)

    Prime an Index using the notation i^3.

    source
    ITensors.setprimeMethod
    setprime(i::Index, plev::Int)

    Return a copy of Index i with its prime level set to plev

    source
    ITensors.settagsMethod
    settags(i::Index, ts)

    Return a copy of Index i with tags replaced by the ones given The ts argument can be a comma-separated string of tags or a TagSet.

    Examples

    julia> i = Index(2, "SpinHalf,Site,n=3")
    +false
    source
    NDTensors.dimMethod
    dim(i::Index)

    Obtain the dimension of an Index.

    For a QN Index, this is the sum of the block dimensions.

    source
    Base.:==Method
    ==(i1::Index, i1::Index)

    Compare indices for equality. First the id's are compared, then the prime levels are compared, and finally the tags are compared.

    source
    ITensors.dirMethod
    dir(i::Index)

    Return the direction of an Index (ITensors.In, ITensors.Out, or ITensors.Neither).

    source

    Priming and tagging methods

    ITensors.primeMethod
    prime(i::Index, plinc::Int = 1)

    Return a copy of Index i with its prime level incremented by the amount plinc

    source
    Base.:^Method
    ^(i::Index, pl::Int)

    Prime an Index using the notation i^3.

    source
    ITensors.setprimeMethod
    setprime(i::Index, plev::Int)

    Return a copy of Index i with its prime level set to plev

    source
    ITensors.settagsMethod
    settags(i::Index, ts)

    Return a copy of Index i with tags replaced by the ones given The ts argument can be a comma-separated string of tags or a TagSet.

    Examples

    julia> i = Index(2, "SpinHalf,Site,n=3")
     (dim=2|id=543|"Site,SpinHalf,n=3")
     
     julia> hastags(i, "Link")
    @@ -64,7 +64,7 @@
     true
     
     julia> hastags(j, "n=4,Link")
    -true
    source
    ITensors.TagSets.addtagsMethod
    addtags(i::Index,ts)

    Return a copy of Index i with the specified tags added to the existing ones. The ts argument can be a comma-separated string of tags or a TagSet.

    source
    ITensors.TagSets.removetagsMethod
    removetags(i::Index, ts)

    Return a copy of Index i with the specified tags removed. The ts argument can be a comma-separated string of tags or a TagSet.

    source
    ITensors.TagSets.addtagsMethod
    addtags(i::Index,ts)

    Return a copy of Index i with the specified tags added to the existing ones. The ts argument can be a comma-separated string of tags or a TagSet.

    source
    ITensors.TagSets.removetagsMethod
    removetags(i::Index, ts)

    Return a copy of Index i with the specified tags removed. The ts argument can be a comma-separated string of tags or a TagSet.

    source
    ITensors.TagSets.replacetagsMethod
    replacetags(i::Index, tsold, tsnew)
     
     replacetags(i::Index, tsold => tsnew)

    If the tag set of i contains the tags specified by tsold, replaces these with the tags specified by tsnew, preserving any other tags. The arguments tsold and tsnew can be comma-separated strings of tags, or TagSet objects.

    Examples

    julia> i = Index(2; tags="l,x", plev=1)
     (dim=2|id=83|"l,x")'
    @@ -73,4 +73,4 @@
     (dim=2|id=83|"m,x")'
     
     julia> replacetags(i, "l" => "m")
    -(dim=2|id=83|"m,x")'
    source

    Methods

    NDTensors.simMethod
    sim(i::Index; tags = tags(i), plev = plev(i), dir = dir(i))

    Produces an Index with the same properties (dimension or QN structure) but with a new id.

    source

    Iterating

    ITensors.eachvalMethod
    eachval(i::Index)

    Create an iterator whose values range over the dimension of the provided Index.

    source
    ITensors.eachindvalMethod
    eachindval(i::Index)

    Create an iterator whose values are Pairs of the form i=>n with n from 1:dim(i). This iterator is useful for accessing elements of an ITensor in a loop without needing to know the ordering of the indices. See also eachindval(is::Index...).

    source
    +(dim=2|id=83|"m,x")'
    source

    Methods

    NDTensors.simMethod
    sim(i::Index; tags = tags(i), plev = plev(i), dir = dir(i))

    Produces an Index with the same properties (dimension or QN structure) but with a new id.

    source

    Iterating

    ITensors.eachvalMethod
    eachval(i::Index)

    Create an iterator whose values range over the dimension of the provided Index.

    source
    ITensors.eachindvalMethod
    eachindval(i::Index)

    Create an iterator whose values are Pairs of the form i=>n with n from 1:dim(i). This iterator is useful for accessing elements of an ITensor in a loop without needing to know the ordering of the indices. See also eachindval(is::Index...).

    source
    diff --git a/previews/PR1410/MPSandMPO.html b/previews/PR1410/MPSandMPO.html index 004fb9c52b..f092b7ab74 100644 --- a/previews/PR1410/MPSandMPO.html +++ b/previews/PR1410/MPSandMPO.html @@ -1,9 +1,9 @@ -MPS and MPO · ITensors.jl

    MPS and MPO

    Types

    MPS Constructors

    ITensors.ITensorMPS.MPSMethod
    MPS([::Type{ElT} = Float64, ]sites; linkdims=1)

    Construct an MPS filled with Empty ITensors of type ElT from a collection of indices.

    Optionally specify the link dimension with the keyword argument linkdims, which by default is 1.

    In the future we may generalize linkdims to allow specifying each individual link dimension as a vector, and additionally allow specifying quantum numbers.

    source
    ITensors.ITensorMPS.randomMPSMethod
    randomMPS(sites::Vector{<:Index}; linkdims=1)
    -randomMPS(eltype::Type{<:Number}, sites::Vector{<:Index}; linkdims=1)

    Construct a random MPS with link dimension linkdims which by default has element type Float64.

    linkdims can also accept a Vector{Int} with length(linkdims) == length(sites) - 1 for constructing an MPS with non-uniform bond dimension.

    source
    ITensors.ITensorMPS.randomMPSMethod
    randomMPS(eltype::Type{<:Number}, sites::Vector{<:Index}; linkdims=1)

    Construct a random MPS with link dimension linkdims of type eltype.

    linkdims can also accept a Vector{Int} with length(linkdims) == length(sites) - 1 for constructing an MPS with non-uniform bond dimension.

    source
    ITensors.ITensorMPS.randomMPSMethod
    randomMPS(sites::Vector{<:Index}, state; linkdims=1)

    Construct a real, random MPS with link dimension linkdims, made by randomizing an initial product state specified by state. This version of randomMPS is necessary when creating QN-conserving random MPS (consisting of QNITensors). The initial state array provided determines the total QN of the resulting random MPS.

    source
    ITensors.ITensorMPS.MPSMethod
    MPS(sites::Vector{<:Index},states)

    Construct a product state MPS having site indices sites, and which corresponds to the initial state given by the array states. The states array may consist of either an array of integers or strings, as recognized by the state function defined for the relevant Index tag type.

    Examples

    N = 10
    +MPS and MPO · ITensors.jl

    MPS and MPO

    Types

    MPS Constructors

    ITensors.ITensorMPS.MPSMethod
    MPS([::Type{ElT} = Float64, ]sites; linkdims=1)

    Construct an MPS filled with Empty ITensors of type ElT from a collection of indices.

    Optionally specify the link dimension with the keyword argument linkdims, which by default is 1.

    In the future we may generalize linkdims to allow specifying each individual link dimension as a vector, and additionally allow specifying quantum numbers.

    source
    ITensors.ITensorMPS.randomMPSMethod
    randomMPS(sites::Vector{<:Index}; linkdims=1)
    +randomMPS(eltype::Type{<:Number}, sites::Vector{<:Index}; linkdims=1)

    Construct a random MPS with link dimension linkdims which by default has element type Float64.

    linkdims can also accept a Vector{Int} with length(linkdims) == length(sites) - 1 for constructing an MPS with non-uniform bond dimension.

    source
    ITensors.ITensorMPS.randomMPSMethod
    randomMPS(eltype::Type{<:Number}, sites::Vector{<:Index}; linkdims=1)

    Construct a random MPS with link dimension linkdims of type eltype.

    linkdims can also accept a Vector{Int} with length(linkdims) == length(sites) - 1 for constructing an MPS with non-uniform bond dimension.

    source
    ITensors.ITensorMPS.randomMPSMethod
    randomMPS(sites::Vector{<:Index}, state; linkdims=1)

    Construct a real, random MPS with link dimension linkdims, made by randomizing an initial product state specified by state. This version of randomMPS is necessary when creating QN-conserving random MPS (consisting of QNITensors). The initial state array provided determines the total QN of the resulting random MPS.

    source
    ITensors.ITensorMPS.MPSMethod
    MPS(sites::Vector{<:Index},states)

    Construct a product state MPS having site indices sites, and which corresponds to the initial state given by the array states. The states array may consist of either an array of integers or strings, as recognized by the state function defined for the relevant Index tag type.

    Examples

    N = 10
     sites = siteinds("S=1/2", N)
     states = [isodd(n) ? "Up" : "Dn" for n in 1:N]
    -psi = MPS(sites, states)
    source
    ITensors.ITensorMPS.MPSMethod
    MPS(::Type{T},
         sites::Vector{<:Index},
         states::Union{Vector{String},
                       Vector{Int},
    @@ -12,7 +12,7 @@
     sites = siteinds("S=1/2", N)
     states = [isodd(n) ? "Up" : "Dn" for n in 1:N]
     psi = MPS(ComplexF64, sites, states)
    -phi = MPS(sites, "Up")
    source
    ITensors.ITensorMPS.MPSMethod
    MPS(ivals::Vector{<:Pair{<:Index}})

    Construct a product state MPS with element type Float64 and nonzero values determined from the input IndexVals.

    source
    ITensors.ITensorMPS.MPSMethod
    MPS(::Type{T<:Number}, ivals::Vector{<:Pair{<:Index}})

    Construct a product state MPS with element type T and nonzero values determined from the input IndexVals.

    source

    MPO Constructors

    ITensors.ITensorMPS.MPOMethod
    MPO([::Type{ElT} = Float64}, ]sites, ops::Vector{String})

    Make an MPO with pairs of sites s[i] and s[i]' and operators ops on each site.

    source
    ITensors.ITensorMPS.MPOMethod
    MPO([::Type{ElT} = Float64, ]sites, op::String)

    Make an MPO with pairs of sites s[i] and s[i]' and operator op on every site.

    source

    Copying behavior

    ITensors.ITensorMPS.MPSMethod
    MPS(ivals::Vector{<:Pair{<:Index}})

    Construct a product state MPS with element type Float64 and nonzero values determined from the input IndexVals.

    source
    ITensors.ITensorMPS.MPSMethod
    MPS(::Type{T<:Number}, ivals::Vector{<:Pair{<:Index}})

    Construct a product state MPS with element type T and nonzero values determined from the input IndexVals.

    source

    MPO Constructors

    ITensors.ITensorMPS.MPOMethod
    MPO([::Type{ElT} = Float64}, ]sites, ops::Vector{String})

    Make an MPO with pairs of sites s[i] and s[i]' and operators ops on each site.

    source
    ITensors.ITensorMPS.MPOMethod
    MPO([::Type{ElT} = Float64, ]sites, op::String)

    Make an MPO with pairs of sites s[i] and s[i]' and operator op on every site.

    source

    Copying behavior

    Base.copyMethod
    copy(::MPS)
     copy(::MPO)

    Make a shallow copy of an MPS or MPO. By shallow copy, it means that a new MPS/MPO is returned, but the data of the tensors are still shared between the returned MPS/MPO and the original MPS/MPO.

    Therefore, replacing an entire tensor of the returned MPS/MPO will not modify the input MPS/MPO, but modifying the data of the returned MPS/MPO will modify the input MPS/MPO.

    Use deepcopy for an alternative that copies the ITensors as well.

    Examples

    julia> using ITensors
     
     julia> s = siteinds("S=1/2", 3);
    @@ -40,7 +40,7 @@
     3.0000000000000004
     
     julia> norm(M3)
    -3.0000000000000004
    source
    Base.deepcopyMethod
    deepcopy(::MPS)
     deepcopy(::MPO)

    Make a deep copy of an MPS or MPO. By deep copy, it means that a new MPS/MPO is returned that doesn't share any data with the input MPS/MPO.

    Therefore, modifying the resulting MPS/MPO will note modify the original MPS/MPO.

    Use copy for an alternative that performs a shallow copy that avoids copying the ITensor data.

    Examples

    julia> using ITensors
     
     julia> s = siteinds("S=1/2", 3);
    @@ -68,19 +68,19 @@
     3.0
     
     julia> norm(M3)
    -3.0
    source

    Properties

    Base.eltypeMethod
    eltype(m::MPS)
    -eltype(m::MPO)

    The element type of the MPS/MPO. Always returns ITensor.

    For the element type of the ITensors of the MPS/MPO, use promote_itensor_eltype.

    source

    Properties

    Base.eltypeMethod
    eltype(m::MPS)
    +eltype(m::MPO)

    The element type of the MPS/MPO. Always returns ITensor.

    For the element type of the ITensors of the MPS/MPO, use promote_itensor_eltype.

    source
    ITensors.fluxMethod
    flux(M::MPS)
     
     flux(M::MPO)
     
     totalqn(M::MPS)
     
    -totalqn(M::MPO)

    For an MPS or MPO which conserves quantum numbers, compute the total QN flux. For a tensor network such as an MPS or MPO, the flux is the sum of fluxes of each of the tensors in the network. The name totalqn is an alias for flux.

    source
    ITensors.hasqnsMethod
    hasqns(M::MPS)
    +totalqn(M::MPO)

    For an MPS or MPO which conserves quantum numbers, compute the total QN flux. For a tensor network such as an MPS or MPO, the flux is the sum of fluxes of each of the tensors in the network. The name totalqn is an alias for flux.

    source
    ITensors.hasqnsMethod
    hasqns(M::MPS)
     
    -hasqns(M::MPO)

    Return true if the MPS or MPO has tensors which carry quantum numbers.

    source
    ITensors.ITensorMPS.maxlinkdimMethod
    maxlinkdim(M::MPS)
    -maxlinkdim(M::MPO)

    Get the maximum link dimension of the MPS or MPO.

    The minimum this will return is 1, even if there are no link indices.

    source

    Obtaining and finding indices

    ITensors.SiteTypes.siteindsMethod
    siteinds(commoninds, A::MPO, B::MPS, j::Integer; kwargs...)
    -siteinds(commonind, A::MPO, B::MPO, j::Integer; kwargs...)

    Get the site index (or indices) of the jth MPO tensor of A that is shared with MPS/MPO B.

    source
    ITensors.SiteTypes.siteindsMethod
    siteinds(uniqueinds, A::MPO, B::MPS, j::Integer; kwargs...)
    -siteinds(uniqueind, A::MPO, B::MPS, j::Integer; kwargs...)

    Get the site index (or indices) of MPO A that is unique to A (not shared with MPS/MPO B).

    source
    ITensors.ITensorMPS.findsiteFunction
    findsite(M::Union{MPS, MPO}, is)

    Return the first site of the MPS or MPO that has at least one Index in common with the Index or collection of indices is.

    To find all sites with common indices with is, use the findsites function.

    Examples

    s = siteinds("S=1/2", 5)
    +hasqns(M::MPO)

    Return true if the MPS or MPO has tensors which carry quantum numbers.

    source
    ITensors.ITensorMPS.maxlinkdimMethod
    maxlinkdim(M::MPS)
    +maxlinkdim(M::MPO)

    Get the maximum link dimension of the MPS or MPO.

    The minimum this will return is 1, even if there are no link indices.

    source

    Obtaining and finding indices

    ITensors.SiteTypes.siteindsMethod
    siteinds(commoninds, A::MPO, B::MPS, j::Integer; kwargs...)
    +siteinds(commonind, A::MPO, B::MPO, j::Integer; kwargs...)

    Get the site index (or indices) of the jth MPO tensor of A that is shared with MPS/MPO B.

    source
    ITensors.SiteTypes.siteindsMethod
    siteinds(uniqueinds, A::MPO, B::MPS, j::Integer; kwargs...)
    +siteinds(uniqueind, A::MPO, B::MPS, j::Integer; kwargs...)

    Get the site index (or indices) of MPO A that is unique to A (not shared with MPS/MPO B).

    source
    ITensors.ITensorMPS.findsiteFunction
    findsite(M::Union{MPS, MPO}, is)

    Return the first site of the MPS or MPO that has at least one Index in common with the Index or collection of indices is.

    To find all sites with common indices with is, use the findsites function.

    Examples

    s = siteinds("S=1/2", 5)
     ψ = randomMPS(s)
     findsite(ψ, s[3]) == 3
     findsite(ψ, (s[3], s[4])) == 3
    @@ -89,7 +89,7 @@
     findsite(M, s[4]) == 4
     findsite(M, s[4]') == 4
     findsite(M, (s[4]', s[4])) == 4
    -findsite(M, (s[4]', s[3])) == 3
    source
    ITensors.ITensorMPS.findsitesFunction
    findsites(M::Union{MPS, MPO}, is)

    Return the sites of the MPS or MPO that have indices in common with the collection of site indices is.

    Examples

    s = siteinds("S=1/2", 5)
    +findsite(M, (s[4]', s[3])) == 3
    source
    ITensors.ITensorMPS.findsitesFunction
    findsites(M::Union{MPS, MPO}, is)

    Return the sites of the MPS or MPO that have indices in common with the collection of site indices is.

    Examples

    s = siteinds("S=1/2", 5)
     ψ = randomMPS(s)
     findsites(ψ, s[3]) == [3]
     findsites(ψ, (s[4], s[1])) == [1, 4]
    @@ -98,38 +98,38 @@
     findsites(M, s[4]) == [4]
     findsites(M, s[4]') == [4]
     findsites(M, (s[4]', s[4])) == [4]
    -findsites(M, (s[4]', s[3])) == [3, 4]
    source
    ITensors.ITensorMPS.firstsiteindsFunction
    firstsiteinds(M::MPO; kwargs...)

    Get a Vector of the first site Index found on each site of M.

    By default, it finds the first site Index with prime level 0.

    source
    ITensors.ITensorMPS.linkindMethod
    linkind(M::MPS, j::Integer)
    -linkind(M::MPO, j::Integer)

    Get the link or bond Index connecting the MPS or MPO tensor on site j to site j+1.

    If there is no link Index, return nothing.

    source
    ITensors.SiteTypes.siteindMethod
    siteind(::typeof(first), M::Union{MPS,MPO}, j::Integer; kwargs...)

    Return the first site Index found on the MPS or MPO (the first Index unique to the jth MPS/MPO tensor).

    You can choose different filters, like prime level and tags, with the kwargs.

    source
    ITensors.SiteTypes.siteindsMethod
    siteinds(M::MPS)
    -siteinds(::typeof(first), M::MPS)

    Get a vector of the first site Index found on each tensor of the MPS.

    siteinds(::typeof(only), M::MPS)

    Get a vector of the only site Index found on each tensor of the MPS. Errors if more than one is found.

    siteinds(::typeof(all), M::MPS)

    Get a vector of the all site Indices found on each tensor of the MPS. Returns a Vector of IndexSets.

    source
    ITensors.SiteTypes.siteindMethod
    siteind(M::MPO, j::Int; plev = 0, kwargs...)

    Get the first site Index of the MPO found, by default with prime level 0.

    source
    ITensors.SiteTypes.siteindsMethod
    siteinds(M::Union{MPS, MPO}}, j::Integer; kwargs...)

    Return the site Indices found of the MPO or MPO at the site j as an IndexSet.

    Optionally filter prime tags and prime levels with keyword arguments like plev and tags.

    source

    Priming and tagging

    ITensors.primeMethod
    prime[!](M::MPS, args...; kwargs...)
    -prime[!](M::MPO, args...; kwargs...)

    Apply prime to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.primeMethod
    prime[!](siteinds, M::MPS, args...; kwargs...)
    -prime[!](siteinds, M::MPO, args...; kwargs...)

    Apply prime to all site indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.primeMethod
    prime[!](linkinds, M::MPS, args...; kwargs...)
    -prime[!](linkinds, M::MPO, args...; kwargs...)

    Apply prime to all link indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.primeMethod
    prime[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)
    -prime[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)

    Apply prime to the site indices that are shared by M1 and M2.

    Returns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.primeMethod
    prime[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)

    Apply prime to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.

    The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.swapprimeMethod
    swapprime[!](M::MPS, args...; kwargs...)
    -swapprime[!](M::MPO, args...; kwargs...)

    Apply swapprime to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.setprimeMethod
    setprime[!](M::MPS, args...; kwargs...)
    -setprime[!](M::MPO, args...; kwargs...)

    Apply setprime to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.setprimeMethod
    setprime[!](siteinds, M::MPS, args...; kwargs...)
    -setprime[!](siteinds, M::MPO, args...; kwargs...)

    Apply setprime to all site indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.setprimeMethod
    setprime[!](linkinds, M::MPS, args...; kwargs...)
    -setprime[!](linkinds, M::MPO, args...; kwargs...)

    Apply setprime to all link indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.setprimeMethod
    setprime[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)
    -setprime[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)

    Apply setprime to the site indices that are shared by M1 and M2.

    Returns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.setprimeMethod
    setprime[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)

    Apply setprime to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.

    The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.noprimeMethod
    noprime[!](M::MPS, args...; kwargs...)
    -noprime[!](M::MPO, args...; kwargs...)

    Apply noprime to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.noprimeMethod
    noprime[!](siteinds, M::MPS, args...; kwargs...)
    -noprime[!](siteinds, M::MPO, args...; kwargs...)

    Apply noprime to all site indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.noprimeMethod
    noprime[!](linkinds, M::MPS, args...; kwargs...)
    -noprime[!](linkinds, M::MPO, args...; kwargs...)

    Apply noprime to all link indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.noprimeMethod
    noprime[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)
    -noprime[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)

    Apply noprime to the site indices that are shared by M1 and M2.

    Returns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.noprimeMethod
    noprime[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)

    Apply noprime to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.

    The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.addtagsMethod
    addtags[!](M::MPS, args...; kwargs...)
    -addtags[!](M::MPO, args...; kwargs...)

    Apply addtags to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.TagSets.addtagsMethod
    addtags[!](siteinds, M::MPS, args...; kwargs...)
    -addtags[!](siteinds, M::MPO, args...; kwargs...)

    Apply addtags to all site indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.addtagsMethod
    addtags[!](linkinds, M::MPS, args...; kwargs...)
    -addtags[!](linkinds, M::MPO, args...; kwargs...)

    Apply addtags to all link indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.addtagsMethod
    addtags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)
    -addtags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)

    Apply addtags to the site indices that are shared by M1 and M2.

    Returns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.addtagsMethod
    addtags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)

    Apply addtags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.

    The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.removetagsMethod
    removetags[!](M::MPS, args...; kwargs...)
    -removetags[!](M::MPO, args...; kwargs...)

    Apply removetags to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.TagSets.removetagsMethod
    removetags[!](siteinds, M::MPS, args...; kwargs...)
    -removetags[!](siteinds, M::MPO, args...; kwargs...)

    Apply removetags to all site indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.removetagsMethod
    removetags[!](linkinds, M::MPS, args...; kwargs...)
    -removetags[!](linkinds, M::MPO, args...; kwargs...)

    Apply removetags to all link indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.removetagsMethod
    removetags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)
    -removetags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)

    Apply removetags to the site indices that are shared by M1 and M2.

    Returns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.removetagsMethod
    removetags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)

    Apply removetags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.

    The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.replacetagsMethod
    replacetags[!](M::MPS, args...; kwargs...)
    -replacetags[!](M::MPO, args...; kwargs...)

    Apply replacetags to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.TagSets.replacetagsMethod
    replacetags[!](siteinds, M::MPS, args...; kwargs...)
    -replacetags[!](siteinds, M::MPO, args...; kwargs...)

    Apply replacetags to all site indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.replacetagsMethod
    replacetags[!](linkinds, M::MPS, args...; kwargs...)
    -replacetags[!](linkinds, M::MPO, args...; kwargs...)

    Apply replacetags to all link indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.replacetagsMethod
    replacetags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)
    -replacetags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)

    Apply replacetags to the site indices that are shared by M1 and M2.

    Returns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.replacetagsMethod
    replacetags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)

    Apply replacetags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.

    The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.settagsMethod
    settags[!](M::MPS, args...; kwargs...)
    -settags[!](M::MPO, args...; kwargs...)

    Apply settags to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.settagsMethod
    settags[!](siteinds, M::MPS, args...; kwargs...)
    -settags[!](siteinds, M::MPO, args...; kwargs...)

    Apply settags to all site indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.settagsMethod
    settags[!](linkinds, M::MPS, args...; kwargs...)
    -settags[!](linkinds, M::MPO, args...; kwargs...)

    Apply settags to all link indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.settagsMethod
    settags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)
    -settags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)

    Apply settags to the site indices that are shared by M1 and M2.

    Returns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.settagsMethod
    settags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)

    Apply settags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.

    The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source

    Operations

    ITensors.ITensorMPS.firstsiteindsFunction
    firstsiteinds(M::MPO; kwargs...)

    Get a Vector of the first site Index found on each site of M.

    By default, it finds the first site Index with prime level 0.

    source
    ITensors.ITensorMPS.linkindMethod
    linkind(M::MPS, j::Integer)
    +linkind(M::MPO, j::Integer)

    Get the link or bond Index connecting the MPS or MPO tensor on site j to site j+1.

    If there is no link Index, return nothing.

    source
    ITensors.SiteTypes.siteindMethod
    siteind(::typeof(first), M::Union{MPS,MPO}, j::Integer; kwargs...)

    Return the first site Index found on the MPS or MPO (the first Index unique to the jth MPS/MPO tensor).

    You can choose different filters, like prime level and tags, with the kwargs.

    source
    ITensors.SiteTypes.siteindsMethod
    siteinds(M::MPS)
    +siteinds(::typeof(first), M::MPS)

    Get a vector of the first site Index found on each tensor of the MPS.

    siteinds(::typeof(only), M::MPS)

    Get a vector of the only site Index found on each tensor of the MPS. Errors if more than one is found.

    siteinds(::typeof(all), M::MPS)

    Get a vector of the all site Indices found on each tensor of the MPS. Returns a Vector of IndexSets.

    source
    ITensors.SiteTypes.siteindMethod
    siteind(M::MPO, j::Int; plev = 0, kwargs...)

    Get the first site Index of the MPO found, by default with prime level 0.

    source
    ITensors.SiteTypes.siteindsMethod
    siteinds(M::Union{MPS, MPO}}, j::Integer; kwargs...)

    Return the site Indices found of the MPO or MPO at the site j as an IndexSet.

    Optionally filter prime tags and prime levels with keyword arguments like plev and tags.

    source

    Priming and tagging

    ITensors.primeMethod
    prime[!](M::MPS, args...; kwargs...)
    +prime[!](M::MPO, args...; kwargs...)

    Apply prime to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.primeMethod
    prime[!](siteinds, M::MPS, args...; kwargs...)
    +prime[!](siteinds, M::MPO, args...; kwargs...)

    Apply prime to all site indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.primeMethod
    prime[!](linkinds, M::MPS, args...; kwargs...)
    +prime[!](linkinds, M::MPO, args...; kwargs...)

    Apply prime to all link indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.primeMethod
    prime[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)
    +prime[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)

    Apply prime to the site indices that are shared by M1 and M2.

    Returns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.primeMethod
    prime[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)

    Apply prime to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.

    The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.swapprimeMethod
    swapprime[!](M::MPS, args...; kwargs...)
    +swapprime[!](M::MPO, args...; kwargs...)

    Apply swapprime to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.setprimeMethod
    setprime[!](M::MPS, args...; kwargs...)
    +setprime[!](M::MPO, args...; kwargs...)

    Apply setprime to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.setprimeMethod
    setprime[!](siteinds, M::MPS, args...; kwargs...)
    +setprime[!](siteinds, M::MPO, args...; kwargs...)

    Apply setprime to all site indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.setprimeMethod
    setprime[!](linkinds, M::MPS, args...; kwargs...)
    +setprime[!](linkinds, M::MPO, args...; kwargs...)

    Apply setprime to all link indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.setprimeMethod
    setprime[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)
    +setprime[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)

    Apply setprime to the site indices that are shared by M1 and M2.

    Returns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.setprimeMethod
    setprime[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)

    Apply setprime to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.

    The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.noprimeMethod
    noprime[!](M::MPS, args...; kwargs...)
    +noprime[!](M::MPO, args...; kwargs...)

    Apply noprime to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.noprimeMethod
    noprime[!](siteinds, M::MPS, args...; kwargs...)
    +noprime[!](siteinds, M::MPO, args...; kwargs...)

    Apply noprime to all site indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.noprimeMethod
    noprime[!](linkinds, M::MPS, args...; kwargs...)
    +noprime[!](linkinds, M::MPO, args...; kwargs...)

    Apply noprime to all link indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.noprimeMethod
    noprime[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)
    +noprime[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)

    Apply noprime to the site indices that are shared by M1 and M2.

    Returns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.noprimeMethod
    noprime[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)

    Apply noprime to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.

    The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.addtagsMethod
    addtags[!](M::MPS, args...; kwargs...)
    +addtags[!](M::MPO, args...; kwargs...)

    Apply addtags to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.TagSets.addtagsMethod
    addtags[!](siteinds, M::MPS, args...; kwargs...)
    +addtags[!](siteinds, M::MPO, args...; kwargs...)

    Apply addtags to all site indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.addtagsMethod
    addtags[!](linkinds, M::MPS, args...; kwargs...)
    +addtags[!](linkinds, M::MPO, args...; kwargs...)

    Apply addtags to all link indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.addtagsMethod
    addtags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)
    +addtags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)

    Apply addtags to the site indices that are shared by M1 and M2.

    Returns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.addtagsMethod
    addtags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)

    Apply addtags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.

    The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.removetagsMethod
    removetags[!](M::MPS, args...; kwargs...)
    +removetags[!](M::MPO, args...; kwargs...)

    Apply removetags to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.TagSets.removetagsMethod
    removetags[!](siteinds, M::MPS, args...; kwargs...)
    +removetags[!](siteinds, M::MPO, args...; kwargs...)

    Apply removetags to all site indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.removetagsMethod
    removetags[!](linkinds, M::MPS, args...; kwargs...)
    +removetags[!](linkinds, M::MPO, args...; kwargs...)

    Apply removetags to all link indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.removetagsMethod
    removetags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)
    +removetags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)

    Apply removetags to the site indices that are shared by M1 and M2.

    Returns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.removetagsMethod
    removetags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)

    Apply removetags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.

    The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.replacetagsMethod
    replacetags[!](M::MPS, args...; kwargs...)
    +replacetags[!](M::MPO, args...; kwargs...)

    Apply replacetags to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.TagSets.replacetagsMethod
    replacetags[!](siteinds, M::MPS, args...; kwargs...)
    +replacetags[!](siteinds, M::MPO, args...; kwargs...)

    Apply replacetags to all site indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.replacetagsMethod
    replacetags[!](linkinds, M::MPS, args...; kwargs...)
    +replacetags[!](linkinds, M::MPO, args...; kwargs...)

    Apply replacetags to all link indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.replacetagsMethod
    replacetags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)
    +replacetags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)

    Apply replacetags to the site indices that are shared by M1 and M2.

    Returns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.TagSets.replacetagsMethod
    replacetags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)

    Apply replacetags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.

    The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.settagsMethod
    settags[!](M::MPS, args...; kwargs...)
    +settags[!](M::MPO, args...; kwargs...)

    Apply settags to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    ITensors.settagsMethod
    settags[!](siteinds, M::MPS, args...; kwargs...)
    +settags[!](siteinds, M::MPO, args...; kwargs...)

    Apply settags to all site indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.settagsMethod
    settags[!](linkinds, M::MPS, args...; kwargs...)
    +settags[!](linkinds, M::MPO, args...; kwargs...)

    Apply settags to all link indices of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors.

    source
    ITensors.settagsMethod
    settags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)
    +settags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)

    Apply settags to the site indices that are shared by M1 and M2.

    Returns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source
    ITensors.settagsMethod
    settags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)

    Apply settags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.

    The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.

    source

    Operations

    ITensors.ITensorMPS.expectMethod
    expect(psi::MPS, op::AbstractString...; kwargs...)
     expect(psi::MPS, op::Matrix{<:Number}...; kwargs...)
     expect(psi::MPS, ops; kwargs...)

    Given an MPS psi and a single operator name, returns a vector of the expected value of the operator on each site of the MPS.

    If multiple operator names are provided, returns a tuple of expectation value vectors.

    If a container of operator names is provided, returns the same type of container with names replaced by vectors of expectation values.

    Optional Keyword Arguments

    • sites = 1:length(psi): compute expected values only for sites in the given range

    Examples

    N = 10
     
    @@ -144,7 +144,7 @@
     s = siteinds("Electron", N)
     psi = randomMPS(s; linkdims=8)
     dens = expect(psi, "Ntot")
    -updens, dndens = expect(psi, "Nup", "Ndn") # pass more than one operator
    source
    ITensors.ITensorMPS.correlation_matrixMethod
    correlation_matrix(psi::MPS,
                        Op1::AbstractString,
                        Op2::AbstractString;
                        kwargs...)
    @@ -162,14 +162,14 @@
     
     s = siteinds("Electron", N; conserve_qns=true)
     psi = randomMPS(s, n -> isodd(n) ? "Up" : "Dn"; linkdims=m)
    -Cuu = correlation_matrix(psi, "Cdagup", "Cup"; sites=2:8)
    source
    ITensors.dagMethod
    dag[!](M::MPS, args...; kwargs...)
    -dag[!](M::MPO, args...; kwargs...)

    Apply dag to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    NDTensors.denseMethod
    dense(::MPS/MPO)

    Given an MPS (or MPO), return a new MPS (or MPO) having called dense on each ITensor to convert each tensor to use dense storage and remove any QN or other sparse structure information, if it is not dense already.

    source
    ITensors.ITensorMPS.movesiteMethod
    movesite(::Union{MPS, MPO}, n1n2::Pair{Int, Int})

    Create a new MPS/MPO where the site at n1 is moved to n2, for a pair n1n2 = n1 => n2.

    This is done with a series a pairwise swaps, and can introduce a lot of entanglement into your state, so use with caution.

    source
    ITensors.dagMethod
    dag[!](M::MPS, args...; kwargs...)
    +dag[!](M::MPO, args...; kwargs...)

    Apply dag to all ITensors of an MPS/MPO, returning a new MPS/MPO.

    The ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.

    source
    NDTensors.denseMethod
    dense(::MPS/MPO)

    Given an MPS (or MPO), return a new MPS (or MPO) having called dense on each ITensor to convert each tensor to use dense storage and remove any QN or other sparse structure information, if it is not dense already.

    source
    ITensors.ITensorMPS.movesiteMethod
    movesite(::Union{MPS, MPO}, n1n2::Pair{Int, Int})

    Create a new MPS/MPO where the site at n1 is moved to n2, for a pair n1n2 = n1 => n2.

    This is done with a series a pairwise swaps, and can introduce a lot of entanglement into your state, so use with caution.

    source
    ITensors.ITensorMPS.orthogonalize!Function
    orthogonalize!(M::MPS, j::Int; kwargs...)
     orthogonalize(M::MPS, j::Int; kwargs...)
     
     orthogonalize!(M::MPO, j::Int; kwargs...)
    -orthogonalize(M::MPO, j::Int; kwargs...)

    Move the orthogonality center of the MPS to site j. No observable property of the MPS will be changed, and no truncation of the bond indices is performed. Afterward, tensors 1,2,...,j-1 will be left-orthogonal and tensors j+1,j+2,...,N will be right-orthogonal.

    Either modify in-place with orthogonalize! or out-of-place with orthogonalize.

    source
    ITensors.ITensorMPS.replacebond!Method
    replacebond!(M::MPS, b::Int, phi::ITensor; kwargs...)

    Factorize the ITensor phi and replace the ITensors b and b+1 of MPS M with the factors. Choose the orthogonality with ortho="left"/"right".

    source
    ITensors.ITensorMPS.sampleMethod
    sample(m::MPS)

    Given a normalized MPS m with orthocenter(m)==1, returns a Vector{Int} of length(m) corresponding to one sample of the probability distribution defined by squaring the components of the tensor that the MPS represents

    source
    ITensors.ITensorMPS.sample!Method
    sample!(m::MPS)

    Given a normalized MPS m, returns a Vector{Int} of length(m) corresponding to one sample of the probability distribution defined by squaring the components of the tensor that the MPS represents. If the MPS does not have an orthogonality center, orthogonalize!(m,1) will be called before computing the sample.

    source
    ITensors.ITensorMPS.sampleMethod
    sample(M::MPO)

    Given a normalized MPO M, returns a Vector{Int} of length(M) corresponding to one sample of the probability distribution defined by the MPO, treating the MPO as a density matrix.

    The MPO M should have an (approximately) positive spectrum.

    source
    NDTensors.truncate!Function
    truncate!(M::MPS; kwargs...)
    -truncate!(M::MPO; kwargs...)

    Perform a truncation of all bonds of an MPS/MPO, using the truncation parameters (cutoff,maxdim, etc.) provided as keyword arguments.

    Keyword arguments:

    • site_range=1:N - only truncate the MPS bonds between these sites
    source

    Gate evolution

    ITensors.productMethod
    apply(o::ITensor, ψ::Union{MPS, MPO}, [ns::Vector{Int}]; kwargs...)
    -product([...])

    Get the product of the operator o with the MPS/MPO ψ, where the operator is applied to the sites ns. If ns are not specified, the sites are determined by the common indices between o and the site indices of ψ.

    If ns are non-contiguous, the sites of the MPS are moved to be contiguous. By default, the sites are moved back to their original locations. You can leave them where they are by setting the keyword argument move_sites_back to false.

    Keywords

    • cutoff::Real: singular value truncation cutoff.
    • maxdim::Int: maximum MPS/MPO dimension.
    • apply_dag::Bool = false: apply the gate and the dagger of the gate (only relevant for MPO evolution).
    • move_sites_back::Bool = true: after the ITensors are applied to the MPS or MPO, move the sites of the MPS or MPO back to their original locations.
    source
    ITensors.productMethod
    apply(As::Vector{<:ITensor}, M::Union{MPS, MPO}; kwargs...)
    +orthogonalize(M::MPO, j::Int; kwargs...)

    Move the orthogonality center of the MPS to site j. No observable property of the MPS will be changed, and no truncation of the bond indices is performed. Afterward, tensors 1,2,...,j-1 will be left-orthogonal and tensors j+1,j+2,...,N will be right-orthogonal.

    Either modify in-place with orthogonalize! or out-of-place with orthogonalize.

    source
    ITensors.ITensorMPS.replacebond!Method
    replacebond!(M::MPS, b::Int, phi::ITensor; kwargs...)

    Factorize the ITensor phi and replace the ITensors b and b+1 of MPS M with the factors. Choose the orthogonality with ortho="left"/"right".

    source
    ITensors.ITensorMPS.sampleMethod
    sample(m::MPS)

    Given a normalized MPS m with orthocenter(m)==1, returns a Vector{Int} of length(m) corresponding to one sample of the probability distribution defined by squaring the components of the tensor that the MPS represents

    source
    ITensors.ITensorMPS.sample!Method
    sample!(m::MPS)

    Given a normalized MPS m, returns a Vector{Int} of length(m) corresponding to one sample of the probability distribution defined by squaring the components of the tensor that the MPS represents. If the MPS does not have an orthogonality center, orthogonalize!(m,1) will be called before computing the sample.

    source
    ITensors.ITensorMPS.sampleMethod
    sample(M::MPO)

    Given a normalized MPO M, returns a Vector{Int} of length(M) corresponding to one sample of the probability distribution defined by the MPO, treating the MPO as a density matrix.

    The MPO M should have an (approximately) positive spectrum.

    source
    NDTensors.truncate!Function
    truncate!(M::MPS; kwargs...)
    +truncate!(M::MPO; kwargs...)

    Perform a truncation of all bonds of an MPS/MPO, using the truncation parameters (cutoff,maxdim, etc.) provided as keyword arguments.

    Keyword arguments:

    • site_range=1:N - only truncate the MPS bonds between these sites
    source

    Gate evolution

    ITensors.productMethod
    apply(o::ITensor, ψ::Union{MPS, MPO}, [ns::Vector{Int}]; kwargs...)
    +product([...])

    Get the product of the operator o with the MPS/MPO ψ, where the operator is applied to the sites ns. If ns are not specified, the sites are determined by the common indices between o and the site indices of ψ.

    If ns are non-contiguous, the sites of the MPS are moved to be contiguous. By default, the sites are moved back to their original locations. You can leave them where they are by setting the keyword argument move_sites_back to false.

    Keywords

    • cutoff::Real: singular value truncation cutoff.
    • maxdim::Int: maximum MPS/MPO dimension.
    • apply_dag::Bool = false: apply the gate and the dagger of the gate (only relevant for MPO evolution).
    • move_sites_back::Bool = true: after the ITensors are applied to the MPS or MPO, move the sites of the MPS or MPO back to their original locations.
    source
    ITensors.productMethod
    apply(As::Vector{<:ITensor}, M::Union{MPS, MPO}; kwargs...)
     product([...])

    Apply the ITensors As to the MPS or MPO M, treating them as gates or matrices from pairs of prime or unprimed indices.

    Keywords

    • cutoff::Real: singular value truncation cutoff.
    • maxdim::Int: maximum MPS/MPO dimension.
    • apply_dag::Bool = false: apply the gate and the dagger of the gate (only relevant for MPO evolution).
    • move_sites_back::Bool = true: after the ITensor is applied to the MPS or MPO, move the sites of the MPS or MPO back to their original locations.

    Examples

    Apply one-site gates to an MPS:

    N = 3
     
     ITensors.op(::OpName"σx", ::SiteType"S=1/2", s::Index) =
    @@ -232,19 +232,19 @@
           ("expS⋅S", (2, 3), (τ = τ,))]
     ψ0 = MPS(s, n -> n == 1 ? "↓" : "↑")
     expτH = ops(os, s)
    -ψτ = apply(expτH, ψ0)
    source

    Algebra Operations

    ITensors.innerMethod
    inner(A::MPS, B::MPS)
    -inner(A::MPO, B::MPO)

    Compute the inner product ⟨A|B⟩. If A and B are MPOs, computes the Frobenius inner product.

    Use loginner to avoid underflow/overflow for taking overlaps of large MPS or MPO.

    ITensors 0.3

    Before ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.

    Same as dot.

    See also loginner, logdot.

    source
    ITensors.ITensorMPS.loginnerMethod
    loginner(A::MPS, B::MPS)
    -loginner(A::MPO, B::MPO)

    Compute the logarithm of the inner product ⟨A|B⟩. If A and B are MPOs, computes the logarithm of the Frobenius inner product.

    This is useful for larger MPS/MPO, where in the limit of large numbers of sites the inner product can diverge or approach zero.

    ITensors 0.3

    Before ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.

    Same as logdot.

    See also inner, dot.

    source
    ITensors.innerMethod
    inner(y::MPS, A::MPO, x::MPS)

    Compute ⟨y|A|x⟩ = ⟨y|Ax⟩ efficiently and exactly without making any intermediate MPOs. In general it is more efficient and accurate than inner(y, apply(A, x)).

    This is helpful for computing the expectation value of an operator A, which would be:

    inner(x', A, x)

    assuming x is normalized.

    If you want to compute ⟨By|Ax⟩ you can use inner(B::MPO, y::MPS, A::MPO, x::MPS).

    This is helpful for computing the variance of an operator A, which would be:

    inner(A, x, A, x) - inner(x', A, x) ^ 2

    assuming x is normalized.

    ITensors 0.3

    Before ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.

    Same as dot.

    source
    ITensors.innerMethod
    inner(B::MPO, y::MPS, A::MPO, x::MPS)

    Compute ⟨By|A|x⟩ = ⟨By|Ax⟩ efficiently and exactly without making any intermediate MPOs. In general it is more efficient and accurate than inner(apply(B, y), apply(A, x)).

    This is helpful for computing the variance of an operator A, which would be:

    inner(A, x, A, x) - inner(x, A, x) ^ 2
    ITensors 0.3

    Before ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.

    Same as dot.

    source
    LinearAlgebra.normMethod
    norm(A::MPS)
    -norm(A::MPO)

    Compute the norm of the MPS or MPO.

    If the MPS or MPO has a well defined orthogonality center, this reduces to the norm of the orthogonality center tensor. Otherwise, it computes the norm with the full inner product of the MPS/MPO with itself.

    See also lognorm.

    source
    LinearAlgebra.normalizeMethod
    normalize(A::MPS; (lognorm!)=[])
    -normalize(A::MPO; (lognorm!)=[])

    Return a new MPS or MPO A that is the same as the original MPS or MPO but with norm(A) ≈ 1.

    In practice, this evenly spreads lognorm(A) over the tensors within the range of the orthogonality center to avoid numerical overflow in the case of diverging norms.

    See also normalize!, norm, lognorm.

    source

    Algebra Operations

    ITensors.innerMethod
    inner(A::MPS, B::MPS)
    +inner(A::MPO, B::MPO)

    Compute the inner product ⟨A|B⟩. If A and B are MPOs, computes the Frobenius inner product.

    Use loginner to avoid underflow/overflow for taking overlaps of large MPS or MPO.

    ITensors 0.3

    Before ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.

    Same as dot.

    See also loginner, logdot.

    source
    ITensors.ITensorMPS.loginnerMethod
    loginner(A::MPS, B::MPS)
    +loginner(A::MPO, B::MPO)

    Compute the logarithm of the inner product ⟨A|B⟩. If A and B are MPOs, computes the logarithm of the Frobenius inner product.

    This is useful for larger MPS/MPO, where in the limit of large numbers of sites the inner product can diverge or approach zero.

    ITensors 0.3

    Before ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.

    Same as logdot.

    See also inner, dot.

    source
    ITensors.innerMethod
    inner(y::MPS, A::MPO, x::MPS)

    Compute ⟨y|A|x⟩ = ⟨y|Ax⟩ efficiently and exactly without making any intermediate MPOs. In general it is more efficient and accurate than inner(y, apply(A, x)).

    This is helpful for computing the expectation value of an operator A, which would be:

    inner(x', A, x)

    assuming x is normalized.

    If you want to compute ⟨By|Ax⟩ you can use inner(B::MPO, y::MPS, A::MPO, x::MPS).

    This is helpful for computing the variance of an operator A, which would be:

    inner(A, x, A, x) - inner(x', A, x) ^ 2

    assuming x is normalized.

    ITensors 0.3

    Before ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.

    Same as dot.

    source
    ITensors.innerMethod
    inner(B::MPO, y::MPS, A::MPO, x::MPS)

    Compute ⟨By|A|x⟩ = ⟨By|Ax⟩ efficiently and exactly without making any intermediate MPOs. In general it is more efficient and accurate than inner(apply(B, y), apply(A, x)).

    This is helpful for computing the variance of an operator A, which would be:

    inner(A, x, A, x) - inner(x, A, x) ^ 2
    ITensors 0.3

    Before ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.

    Same as dot.

    source
    LinearAlgebra.normMethod
    norm(A::MPS)
    +norm(A::MPO)

    Compute the norm of the MPS or MPO.

    If the MPS or MPO has a well defined orthogonality center, this reduces to the norm of the orthogonality center tensor. Otherwise, it computes the norm with the full inner product of the MPS/MPO with itself.

    See also lognorm.

    source
    LinearAlgebra.normalizeMethod
    normalize(A::MPS; (lognorm!)=[])
    +normalize(A::MPO; (lognorm!)=[])

    Return a new MPS or MPO A that is the same as the original MPS or MPO but with norm(A) ≈ 1.

    In practice, this evenly spreads lognorm(A) over the tensors within the range of the orthogonality center to avoid numerical overflow in the case of diverging norms.

    See also normalize!, norm, lognorm.

    source
    LinearAlgebra.normalize!Method
    normalize!(A::MPS; (lognorm!)=[])
     normalize!(A::MPO; (lognorm!)=[])

    Change the MPS or MPO A in-place such that norm(A) ≈ 1. This modifies the data of the tensors within the orthogonality center.

    In practice, this evenly spreads lognorm(A) over the tensors within the range of the orthogonality center to avoid numerical overflow in the case of diverging norms.

    If the norm of the input MPS or MPO is 0, normalizing is ill-defined. In this case, we just return the original MPS or MPO. You can check for this case as follows:

    s = siteinds("S=1/2", 4)
     ψ = 0 * randomMPS(s)
     lognorm_ψ = []
     normalize!(ψ; (lognorm!)=lognorm_ψ)
    -lognorm_ψ[1] == -Inf # There was an infinite norm

    See also normalize, norm, lognorm.

    source
    ITensors.ITensorMPS.lognormMethod
    lognorm(A::MPS)
    -lognorm(A::MPO)

    Compute the logarithm of the norm of the MPS or MPO.

    This is useful for larger MPS/MPO that are not gauged, where in the limit of large numbers of sites the norm can diverge or approach zero.

    See also norm, logdot.

    source
    ITensors.ITensorMPS.lognormMethod
    lognorm(A::MPS)
    +lognorm(A::MPO)

    Compute the logarithm of the norm of the MPS or MPO.

    This is useful for larger MPS/MPO that are not gauged, where in the limit of large numbers of sites the norm can diverge or approach zero.

    See also norm, logdot.

    source
    Base.:+Method
    +(A::MPS/MPO...; kwargs...)
     add(A::MPS/MPO...; kwargs...)

    Add arbitrary numbers of MPS/MPO with each other, optionally truncating the results.

    A cutoff of 1e-15 is used by default, and in general users should set their own cutoff for their particular application.

    Keywords

    • cutoff::Real: singular value truncation cutoff
    • maxdim::Int: maximum MPS/MPO bond dimension
    • alg = "densitymatrix": "densitymatrix" or "directsum". "densitymatrix" adds the MPS/MPO by adding up and diagoanlizing local density matrices site by site in a single sweep through the system, truncating the density matrix with cutoff and maxdim. "directsum" performs a direct sum of each tensors on each site of the input MPS/MPO being summed. It doesn't perform any truncation, and therefore ignores cutoff and maxdim. The bond dimension of the output is the sum of the bond dimensions of the inputs. You can truncate the resulting MPS/MPO with the truncate! function.

    Examples

    N = 10
     
     s = siteinds("S=1/2", N; conserve_qns = true)
    @@ -280,18 +280,18 @@
     @show inner(ψ, ψ)
     @show inner(ψ₁, ψ₁) + 2 * inner(ψ₁, ψ₂) + inner(ψ₁, ψ₃) +
           2 * inner(ψ₂, ψ₁) + 4 * inner(ψ₂, ψ₂) + 2 * inner(ψ₂, ψ₃) +
    -      inner(ψ₃, ψ₁) + 2 * inner(ψ₃, ψ₂) + inner(ψ₃, ψ₃)
    source
    NDTensors.contractMethod
    contract(ψ::MPS, A::MPO; kwargs...) -> MPS
    +      inner(ψ₃, ψ₁) + 2 * inner(ψ₃, ψ₂) + inner(ψ₃, ψ₃)
    source
    NDTensors.contractMethod
    contract(ψ::MPS, A::MPO; kwargs...) -> MPS
     *(::MPS, ::MPO; kwargs...) -> MPS
     
     contract(A::MPO, ψ::MPS; kwargs...) -> MPS
    -*(::MPO, ::MPS; kwargs...) -> MPS

    Contract the MPO A with the MPS ψ, returning an MPS with the unique site indices of the MPO.

    For example, for an MPO with site indices with prime levels of 1 and 0, such as -s'-A-s-, and an MPS with site indices with prime levels of 0, such as -s-x, the result is an MPS y with site indices with prime levels of 1, -s'-y = -s'-A-s-x.

    Since it is common to contract an MPO with prime levels of 1 and 0 with an MPS with prime level of 0 and want a resulting MPS with prime levels of 0, we provide a convenience function apply:

    apply(A, x; kwargs...) = replaceprime(contract(A, x; kwargs...), 2 => 1)`.

    Choose the method with the method keyword, for example "densitymatrix" and "naive".

    Keywords

    • cutoff::Float64=1e-13: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.
    • maxdim::Int=maxlinkdim(A) * maxlinkdim(ψ)): the maximal bond dimension of the results MPS.
    • mindim::Int=1: the minimal bond dimension of the resulting MPS.
    • normalize::Bool=false: whether or not to normalize the resulting MPS.
    • method::String="densitymatrix": the algorithm to use for the contraction. Currently the options are "densitymatrix", where the network formed by the MPO and MPS is squared and contracted down to a density matrix which is diagonalized iteratively at each site, and "naive", where the MPO and MPS tensor are contracted exactly at each site and then a truncation of the resulting MPS is performed.

    See also apply.

    source
    ITensors.applyMethod
    apply(A::MPO, x::MPS; kwargs...)

    Contract the MPO A with the MPS x and then map the prime level of the resulting MPS back to 0.

    Equivalent to replaceprime(contract(A, x; kwargs...), 2 => 1).

    See also contract for details about the arguments available.

    source
    NDTensors.contractMethod
    contract(A::MPO, B::MPO; kwargs...) -> MPO
    +*(::MPO, ::MPS; kwargs...) -> MPS

    Contract the MPO A with the MPS ψ, returning an MPS with the unique site indices of the MPO.

    For example, for an MPO with site indices with prime levels of 1 and 0, such as -s'-A-s-, and an MPS with site indices with prime levels of 0, such as -s-x, the result is an MPS y with site indices with prime levels of 1, -s'-y = -s'-A-s-x.

    Since it is common to contract an MPO with prime levels of 1 and 0 with an MPS with prime level of 0 and want a resulting MPS with prime levels of 0, we provide a convenience function apply:

    apply(A, x; kwargs...) = replaceprime(contract(A, x; kwargs...), 2 => 1)`.

    Choose the method with the method keyword, for example "densitymatrix" and "naive".

    Keywords

    • cutoff::Float64=1e-13: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.
    • maxdim::Int=maxlinkdim(A) * maxlinkdim(ψ)): the maximal bond dimension of the results MPS.
    • mindim::Int=1: the minimal bond dimension of the resulting MPS.
    • normalize::Bool=false: whether or not to normalize the resulting MPS.
    • method::String="densitymatrix": the algorithm to use for the contraction. Currently the options are "densitymatrix", where the network formed by the MPO and MPS is squared and contracted down to a density matrix which is diagonalized iteratively at each site, and "naive", where the MPO and MPS tensor are contracted exactly at each site and then a truncation of the resulting MPS is performed.

    See also apply.

    source
    ITensors.applyMethod
    apply(A::MPO, x::MPS; kwargs...)

    Contract the MPO A with the MPS x and then map the prime level of the resulting MPS back to 0.

    Equivalent to replaceprime(contract(A, x; kwargs...), 2 => 1).

    See also contract for details about the arguments available.

    source
    NDTensors.contractMethod
    contract(A::MPO, B::MPO; kwargs...) -> MPO
     *(::MPO, ::MPO; kwargs...) -> MPO

    Contract the MPO A with the MPO B, returning an MPO with the site indices that are not shared between A and B.

    If you are contracting two MPOs with the same sets of indices, likely you want to call something like:

    C = contract(A', B; cutoff=1e-12)
     C = replaceprime(C, 2 => 1)

    That is because if MPO A has the index structure -s'-A-s- and MPO B has the Index structure -s'-B-s-, if we only want to contract over on set of the indices, we would do (-s'-A-s-)'-s'-B-s- = -s''-A-s'-s'-B-s- = -s''-C-s-, and then map the prime levels back to pairs of primed and unprimed indices with: replaceprime(-s''-C-s-, 2 => 1) = -s'-C-s-.

    Since this is a common use case, you can use the convenience function:

    C = apply(A, B; cutoff=1e-12)

    which is the same as the code above.

    If you are contracting MPOs that have diverging norms, such as MPOs representing sums of local operators, the truncation can become numerically unstable (see https://arxiv.org/abs/1909.06341 for a more numerically stable alternative). For now, you can use the following options to contract MPOs like that:

    C = contract(A, B; alg="naive", truncate=false)
     # Bring the indices back to pairs of primed and unprimed
    -C = apply(A, B; alg="naive", truncate=false)

    Keywords

    • cutoff::Float64=1e-14: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.
    • maxdim::Int=maxlinkdim(A) * maxlinkdim(B)): the maximal bond dimension of the results MPS.
    • mindim::Int=1: the minimal bond dimension of the resulting MPS.
    • alg="zipup": Either "zipup" or "naive". "zipup" contracts pairs of site tensors and truncates with SVDs in a sweep across the sites, while "naive" first contracts pairs of tensor exactly and then truncates at the end if truncate=true.
    • truncate=true: Enable or disable truncation. If truncate=false, ignore other truncation parameters like cutoff and maxdim. This is most relevant for the "naive" version, if you just want to contract the tensors pairwise exactly. This can be useful if you are contracting MPOs that have diverging norms, such as MPOs originating from sums of local operators.

    See also apply for details about the arguments available.

    source
    ITensors.applyMethod
    apply(A::MPO, B::MPO; kwargs...)

    Contract the MPO A' with the MPO B and then map the prime level of the resulting MPO back to having pairs of indices with prime levels of 1 and 0.

    Equivalent to replaceprime(contract(A', B; kwargs...), 2 => 1).

    See also contract for details about the arguments available.

    source
    ITensors.ITensorMPS.error_contractMethod
    error_contract(y::MPS, A::MPO, x::MPS;
    +C = apply(A, B; alg="naive", truncate=false)

    Keywords

    • cutoff::Float64=1e-14: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.
    • maxdim::Int=maxlinkdim(A) * maxlinkdim(B)): the maximal bond dimension of the results MPS.
    • mindim::Int=1: the minimal bond dimension of the resulting MPS.
    • alg="zipup": Either "zipup" or "naive". "zipup" contracts pairs of site tensors and truncates with SVDs in a sweep across the sites, while "naive" first contracts pairs of tensor exactly and then truncates at the end if truncate=true.
    • truncate=true: Enable or disable truncation. If truncate=false, ignore other truncation parameters like cutoff and maxdim. This is most relevant for the "naive" version, if you just want to contract the tensors pairwise exactly. This can be useful if you are contracting MPOs that have diverging norms, such as MPOs originating from sums of local operators.

    See also apply for details about the arguments available.

    source
    ITensors.applyMethod
    apply(A::MPO, B::MPO; kwargs...)

    Contract the MPO A' with the MPO B and then map the prime level of the resulting MPO back to having pairs of indices with prime levels of 1 and 0.

    Equivalent to replaceprime(contract(A', B; kwargs...), 2 => 1).

    See also contract for details about the arguments available.

    source
    ITensors.ITensorMPS.error_contractMethod
    error_contract(y::MPS, A::MPO, x::MPS;
                    make_inds_match::Bool = true)
     error_contract(y::MPS, x::MPS, x::MPO;
    -               make_inds_match::Bool = true)

    Compute the distance between A|x> and an approximation MPS y: | |y> - A|x> |/| A|x> | = √(1 + (<y|y> - 2*real(<y|A|x>))/<Ax|A|x>).

    If make_inds_match = true, the function attempts match the site indices of y with the site indices of A that are not common with x.

    source
    NDTensors.outerMethod
    outer(x::MPS, y::MPS; <keyword argument>) -> MPO

    Compute the outer product of MPS x and MPS y, returning an MPO approximation. Note that y will be conjugated.

    In Dirac notation, this is the operation |x⟩⟨y|.

    If you want an outer product of an MPS with itself, you should call outer(x', x; kwargs...) so that the resulting MPO has site indices with indices coming in pairs of prime levels of 1 and 0. If not, the site indices won't be unique which would not be an outer product.

    For example:

    s = siteinds("S=1/2", 5)
    +               make_inds_match::Bool = true)

    Compute the distance between A|x> and an approximation MPS y: | |y> - A|x> |/| A|x> | = √(1 + (<y|y> - 2*real(<y|A|x>))/<Ax|A|x>).

    If make_inds_match = true, the function attempts match the site indices of y with the site indices of A that are not common with x.

    source
    NDTensors.outerMethod
    outer(x::MPS, y::MPS; <keyword argument>) -> MPO

    Compute the outer product of MPS x and MPS y, returning an MPO approximation. Note that y will be conjugated.

    In Dirac notation, this is the operation |x⟩⟨y|.

    If you want an outer product of an MPS with itself, you should call outer(x', x; kwargs...) so that the resulting MPO has site indices with indices coming in pairs of prime levels of 1 and 0. If not, the site indices won't be unique which would not be an outer product.

    For example:

    s = siteinds("S=1/2", 5)
     x = randomMPS(s)
     y = randomMPS(s)
     outer(x, y) # Incorrect! Site indices must be unique.
    @@ -302,4 +302,4 @@
     y = convert(MPS, Y)
     outer(x, y) # Incorrect! Site indices must be unique.
     outer(x', y) # Incorrect! Site indices must be unique.
    -outer(addtags(x, "Out"), addtags(y, "In")) # This performs a proper outer product.

    The keyword arguments determine the truncation, and accept the same arguments as contract(::MPO, ::MPO; kwargs...).

    See also apply, contract.

    source
    ITensors.ITensorMPS.projectorMethod
    projector(x::MPS; <keyword argument>) -> MPO

    Computes the projector onto the state x. In Dirac notation, this is the operation |x⟩⟨x|/|⟨x|x⟩|².

    Use keyword arguments to control the level of truncation, which are the same as those accepted by contract(::MPO, ::MPO; kw...).

    Keywords

    • normalize::Bool=true: whether or not to normalize the input MPS before forming the projector. If normalize==false and the input MPS is not already normalized, this function will not output a proper project, and simply outputs outer(x, x) = |x⟩⟨x|, i.e. the projector scaled by norm(x)^2.
    • truncation keyword arguments accepted by contract(::MPO, ::MPO; kw...).

    See also outer, contract.

    source
    +outer(addtags(x, "Out"), addtags(y, "In")) # This performs a proper outer product.

    The keyword arguments determine the truncation, and accept the same arguments as contract(::MPO, ::MPO; kwargs...).

    See also apply, contract.

    source
    ITensors.ITensorMPS.projectorMethod
    projector(x::MPS; <keyword argument>) -> MPO

    Computes the projector onto the state x. In Dirac notation, this is the operation |x⟩⟨x|/|⟨x|x⟩|².

    Use keyword arguments to control the level of truncation, which are the same as those accepted by contract(::MPO, ::MPO; kw...).

    Keywords

    • normalize::Bool=true: whether or not to normalize the input MPS before forming the projector. If normalize==false and the input MPS is not already normalized, this function will not output a proper project, and simply outputs outer(x, x) = |x⟩⟨x|, i.e. the projector scaled by norm(x)^2.
    • truncation keyword arguments accepted by contract(::MPO, ::MPO; kw...).

    See also outer, contract.

    source
    diff --git a/previews/PR1410/Multithreading.html b/previews/PR1410/Multithreading.html index 78d8e3547e..2fa890181c 100644 --- a/previews/PR1410/Multithreading.html +++ b/previews/PR1410/Multithreading.html @@ -36,7 +36,7 @@ $ julia -t 4 -$ JULIA_NUM_THREADS=4 julia

    In addition, we have found that it is best to disable BLAS and Strided multithreading when using block sparse multithreading. You can do that with the commands using LinearAlgebra; BLAS.set_num_threads(1) and ITensors.Strided.disable_threads().

    See also: ITensors.enable_threaded_blocksparse, ITensors.disable_threaded_blocksparse, ITensors.using_threaded_blocksparse.

    source
    enable_threaded_blocksparse(enable::Bool)

    enable_threaded_blocksparse(true) enables threaded block sparse operations (equivalent to enable_threaded_blocksparse()).

    enable_threaded_blocksparse(false) disables threaded block sparse operations (equivalent to enable_threaded_blocksparse()).

    source

    Here is a simple example of using block sparse multithreading to speed up a sparse tensor contraction:

    using BenchmarkTools
    +$ JULIA_NUM_THREADS=4 julia

    In addition, we have found that it is best to disable BLAS and Strided multithreading when using block sparse multithreading. You can do that with the commands using LinearAlgebra; BLAS.set_num_threads(1) and ITensors.Strided.disable_threads().

    See also: ITensors.enable_threaded_blocksparse, ITensors.disable_threaded_blocksparse, ITensors.using_threaded_blocksparse.

    source
    enable_threaded_blocksparse(enable::Bool)

    enable_threaded_blocksparse(true) enables threaded block sparse operations (equivalent to enable_threaded_blocksparse()).

    enable_threaded_blocksparse(false) disables threaded block sparse operations (equivalent to enable_threaded_blocksparse()).

    source

    Here is a simple example of using block sparse multithreading to speed up a sparse tensor contraction:

    using BenchmarkTools
     using ITensors
     using LinearAlgebra
     
    @@ -91,4 +91,4 @@
     Threaded contract:
       5.934 ms (446 allocations: 7.37 MiB)
     
    -C_contract ≈ C_threaded_contract = true

    In addition, we plan to add more threading to other parts of the code beyond contraction (such as SVD) and improve composibility with other forms of threading like BLAS and Strided, so stay tuned!

    +C_contract ≈ C_threaded_contract = true

    In addition, we plan to add more threading to other parts of the code beyond contraction (such as SVD) and improve composibility with other forms of threading like BLAS and Strided, so stay tuned!

    diff --git a/previews/PR1410/Observer.html b/previews/PR1410/Observer.html index dd2befea3b..dbe8ef7159 100644 --- a/previews/PR1410/Observer.html +++ b/previews/PR1410/Observer.html @@ -82,4 +82,4 @@ energy, psi = dmrg(H,psi0; nsweeps, cutoff, maxdim, observer=obs, outputlevel=1) return -end +end diff --git a/previews/PR1410/OpSum.html b/previews/PR1410/OpSum.html index 0117b3d38a..4e12a748d2 100644 --- a/previews/PR1410/OpSum.html +++ b/previews/PR1410/OpSum.html @@ -1,5 +1,5 @@ -OpSum (AutoMPO) · ITensors.jl

    OpSum

    Description

    ITensors.Ops.OpSumType

    An OpSum represents a sum of operator terms.

    Often it is used to create matrix product operator (MPO) approximation of the sum of the terms in the OpSum oject. Each term is a product of local operators specified by names such as "Sz" or "N", times an optional coefficient which can be real or complex.

    Which local operator names are available is determined by the function op associated with the TagType defined by special Index tags, such as "S=1/2", "S=1", "Fermion", and "Electron".

    source

    Methods

    ITensors.ITensorMPS.add!Function
    add!(opsum::OpSum,
    +OpSum (AutoMPO) · ITensors.jl

    OpSum

    Description

    ITensors.Ops.OpSumType

    An OpSum represents a sum of operator terms.

    Often it is used to create matrix product operator (MPO) approximation of the sum of the terms in the OpSum oject. Each term is a product of local operators specified by names such as "Sz" or "N", times an optional coefficient which can be real or complex.

    Which local operator names are available is determined by the function op associated with the TagType defined by special Index tags, such as "S=1/2", "S=1", "Fermion", and "Electron".

    source

    Methods

    ITensors.ITensorMPS.add!Function
    add!(opsum::OpSum,
          op1::String, i1::Int)
     
     add!(opsum::OpSum,
    @@ -25,7 +25,7 @@
     
     opsum += (0.5,"S+",4,"S-",5)
     
    -opsum .+= (0.5,"S+",5,"S-",6)
    source
    ITensors.ITensorMPS.MPOMethod
    MPO(os::OpSum, sites::Vector{<:Index}; splitblocks=true, kwargs...)
     MPO(eltype::Type{<:Number}, os::OpSum, sites::Vector{<:Index}; splitblocks=true, kwargs...)

    Convert an OpSum object os to an MPO, with indices given by sites. The resulting MPO will have the indices sites[1], sites[1]', sites[2], sites[2]' etc. The conversion is done by an algorithm that compresses the MPO resulting from adding the OpSum terms together, often achieving the minimum possible bond dimension.

    Optionally specify the desired element type of the output MPO by passing the type as the first argument.

    The keyword argument splitblocks controls the sparsity of the resulting MPO. With the default splitblocks=true, the link indices of the MPO are split into blocks of dimension 1, potentially making the MPO more sparse.

    With the splitblocks=false, the blocks of the link dimensions are packed as much as possible according to common quantum numbers, making larger blocks. Before ITensors 0.3.19, this was the default output, but we have found that in general MPOs output with splitblocks=true lead to better performance in algorithms like DMRG.

    Examples

    os = OpSum()
     os += "Sz",1,"Sz",2
     os += "Sz",2,"Sz",3
    @@ -34,4 +34,4 @@
     sites = siteinds("S=1/2",4)
     H = MPO(os,sites)
     H = MPO(Float32,os,sites)
    -H = MPO(os,sites; splitblocks=false)
    source
    +H = MPO(os,sites; splitblocks=false)
    source
    diff --git a/previews/PR1410/ProjMPO.html b/previews/PR1410/ProjMPO.html index 593686bb5d..c043aafd06 100644 --- a/previews/PR1410/ProjMPO.html +++ b/previews/PR1410/ProjMPO.html @@ -3,8 +3,8 @@ | | | | | | | | | | | o--o--o--o--o--o--o--o--o--o--o H | | | | | | | | | | | -o--o--o- -o--o--o--o--o--o |psi>source

    Methods

    ITensors.productMethod
    product(P::ProjMPO,v::ITensor)::ITensor
    +o--o--o-      -o--o--o--o--o--o |psi>
    source

    Methods

    ITensors.productMethod
    product(P::ProjMPO,v::ITensor)::ITensor
     
    -(P::ProjMPO)(v::ITensor)

    Efficiently multiply the ProjMPO P by an ITensor v in the sense that the ProjMPO is a generalized square matrix or linear operator and v is a generalized vector in the space where it acts. The returned ITensor will have the same indices as v. The operator overload P(v) is shorthand for product(P,v).

    source
    ITensors.ITensorMPS.position!Method
    position!(P::ProjMPO, psi::MPS, pos::Int)

    Given an MPS psi, shift the projection of the MPO represented by the ProjMPO P such that the set of unprojected sites begins with site pos. This operation efficiently reuses previous projections of the MPO on sites that have already been projected. The MPS psi must have compatible bond indices with the previous projected MPO tensors for this operation to succeed.

    source
    ITensors.ITensorMPS.noisetermMethod
    noiseterm(P::ProjMPO,
    +(P::ProjMPO)(v::ITensor)

    Efficiently multiply the ProjMPO P by an ITensor v in the sense that the ProjMPO is a generalized square matrix or linear operator and v is a generalized vector in the space where it acts. The returned ITensor will have the same indices as v. The operator overload P(v) is shorthand for product(P,v).

    source
    ITensors.ITensorMPS.position!Method
    position!(P::ProjMPO, psi::MPS, pos::Int)

    Given an MPS psi, shift the projection of the MPO represented by the ProjMPO P such that the set of unprojected sites begins with site pos. This operation efficiently reuses previous projections of the MPO on sites that have already been projected. The MPS psi must have compatible bond indices with the previous projected MPO tensors for this operation to succeed.

    source
    ITensors.ITensorMPS.noisetermMethod
    noiseterm(P::ProjMPO,
               phi::ITensor,
    -          ortho::String)

    Return a "noise term" or density matrix perturbation ITensor as proposed in Phys. Rev. B 72, 180403 for aiding convergence of DMRG calculations. The ITensor phi is the contracted product of MPS tensors acted on by the ProjMPO P, and ortho is a String which can take the values "left" or "right" depending on the sweeping direction of the DMRG calculation.

    source

    Properties

    Base.lengthMethod
    length(P::ProjMPO)

    The length of a ProjMPO is the same as the length of the MPO used to construct it

    source
    Base.eltypeMethod
    eltype(P::ProjMPO)

    Deduce the element type (such as Float64 or ComplexF64) of the tensors in the ProjMPO P.

    source
    Base.sizeMethod
    size(P::ProjMPO)

    The size of a ProjMPO are its dimensions (d,d) when viewed as a matrix or linear operator acting on a space of dimension d.

    For example, if a ProjMPO maps from a space with indices (a,s1,s2,b) to the space (a',s1',s2',b') then the size is (d,d) where d = dim(a)*dim(s1)*dim(s1)*dim(b)

    source
    + ortho::String)

    Return a "noise term" or density matrix perturbation ITensor as proposed in Phys. Rev. B 72, 180403 for aiding convergence of DMRG calculations. The ITensor phi is the contracted product of MPS tensors acted on by the ProjMPO P, and ortho is a String which can take the values "left" or "right" depending on the sweeping direction of the DMRG calculation.

    source

    Properties

    Base.lengthMethod
    length(P::ProjMPO)

    The length of a ProjMPO is the same as the length of the MPO used to construct it

    source
    Base.eltypeMethod
    eltype(P::ProjMPO)

    Deduce the element type (such as Float64 or ComplexF64) of the tensors in the ProjMPO P.

    source
    Base.sizeMethod
    size(P::ProjMPO)

    The size of a ProjMPO are its dimensions (d,d) when viewed as a matrix or linear operator acting on a space of dimension d.

    For example, if a ProjMPO maps from a space with indices (a,s1,s2,b) to the space (a',s1',s2',b') then the size is (d,d) where d = dim(a)*dim(s1)*dim(s1)*dim(b)

    source
    diff --git a/previews/PR1410/ProjMPOSum.html b/previews/PR1410/ProjMPOSum.html index d07db8b822..93bf7bdc89 100644 --- a/previews/PR1410/ProjMPOSum.html +++ b/previews/PR1410/ProjMPOSum.html @@ -3,8 +3,8 @@ | | | | | | | | | | | Σⱼ o--o--o--o--o--o--o--o--o--o--o Hⱼ | | | | | | | | | | | - o--o--o- -o--o--o--o--o--o |psi>source

    Methods

    ITensors.productMethod
    product(P::ProjMPOSum,v::ITensor)
    +     o--o--o-      -o--o--o--o--o--o |psi>
    source

    Methods

    ITensors.productMethod
    product(P::ProjMPOSum,v::ITensor)
     
    -(P::ProjMPOSum)(v::ITensor)

    Efficiently multiply the ProjMPOSum P by an ITensor v in the sense that the ProjMPOSum is a generalized square matrix or linear operator and v is a generalized vector in the space where it acts. The returned ITensor will have the same indices as v. The operator overload P(v) is shorthand for product(P,v).

    source
    ITensors.ITensorMPS.position!Method
    position!(P::ProjMPOSum, psi::MPS, pos::Int)

    Given an MPS psi, shift the projection of the MPO represented by the ProjMPOSum P such that the set of unprojected sites begins with site pos. This operation efficiently reuses previous projections of the MPOs on sites that have already been projected. The MPS psi must have compatible bond indices with the previous projected MPO tensors for this operation to succeed.

    source
    ITensors.ITensorMPS.noisetermMethod
    noiseterm(P::ProjMPOSum,
    +(P::ProjMPOSum)(v::ITensor)

    Efficiently multiply the ProjMPOSum P by an ITensor v in the sense that the ProjMPOSum is a generalized square matrix or linear operator and v is a generalized vector in the space where it acts. The returned ITensor will have the same indices as v. The operator overload P(v) is shorthand for product(P,v).

    source
    ITensors.ITensorMPS.position!Method
    position!(P::ProjMPOSum, psi::MPS, pos::Int)

    Given an MPS psi, shift the projection of the MPO represented by the ProjMPOSum P such that the set of unprojected sites begins with site pos. This operation efficiently reuses previous projections of the MPOs on sites that have already been projected. The MPS psi must have compatible bond indices with the previous projected MPO tensors for this operation to succeed.

    source
    ITensors.ITensorMPS.noisetermMethod
    noiseterm(P::ProjMPOSum,
               phi::ITensor,
    -          ortho::String)

    Return a "noise term" or density matrix perturbation ITensor as proposed in Phys. Rev. B 72, 180403 for aiding convergence of DMRG calculations. The ITensor phi is the contracted product of MPS tensors acted on by the ProjMPOSum P, and ortho is a String which can take the values "left" or "right" depending on the sweeping direction of the DMRG calculation.

    source

    Properties

    Base.eltypeMethod
    eltype(P::ProjMPOSum)

    Deduce the element type (such as Float64 or ComplexF64) of the tensors in the ProjMPOSum P.

    source
    Base.sizeMethod
    size(P::ProjMPOSum)

    The size of a ProjMPOSum are its dimensions (d,d) when viewed as a matrix or linear operator acting on a space of dimension d.

    For example, if a ProjMPOSum maps from a space with indices (a,s1,s2,b) to the space (a',s1',s2',b') then the size is (d,d) where d = dim(a)*dim(s1)*dim(s1)*dim(b)

    source
    + ortho::String)

    Return a "noise term" or density matrix perturbation ITensor as proposed in Phys. Rev. B 72, 180403 for aiding convergence of DMRG calculations. The ITensor phi is the contracted product of MPS tensors acted on by the ProjMPOSum P, and ortho is a String which can take the values "left" or "right" depending on the sweeping direction of the DMRG calculation.

    source

    Properties

    Base.eltypeMethod
    eltype(P::ProjMPOSum)

    Deduce the element type (such as Float64 or ComplexF64) of the tensors in the ProjMPOSum P.

    source
    Base.sizeMethod
    size(P::ProjMPOSum)

    The size of a ProjMPOSum are its dimensions (d,d) when viewed as a matrix or linear operator acting on a space of dimension d.

    For example, if a ProjMPOSum maps from a space with indices (a,s1,s2,b) to the space (a',s1',s2',b') then the size is (d,d) where d = dim(a)*dim(s1)*dim(s1)*dim(b)

    source
    diff --git a/previews/PR1410/QN.html b/previews/PR1410/QN.html index 965f7e3c12..c7b0573eca 100644 --- a/previews/PR1410/QN.html +++ b/previews/PR1410/QN.html @@ -1,4 +1,4 @@ -QN · ITensors.jl

    QN

    Description

    ITensors.QuantumNumbers.QNType

    A QN object stores a collection of up to four named values such as ("Sz",1) or ("N",0). These values can include a third integer "m" which makes them obey addition modulo m, for example ("P",1,2) for a value obeying addition mod 2. (The default is regular integer addition).

    Adding or subtracting pairs of QN objects performs addition and subtraction element-wise on each of the named values. If a name is missing from the collection, its value is treated as zero.

    source

    Constructors

    ITensors.QuantumNumbers.QNMethod
    QN(qvs...)

    Construct a QN from a set of up to four named value tuples.

    Examples

    q = QN(("Sz",1))
    +QN · ITensors.jl

    QN

    Description

    ITensors.QuantumNumbers.QNType

    A QN object stores a collection of up to four named values such as ("Sz",1) or ("N",0). These values can include a third integer "m" which makes them obey addition modulo m, for example ("P",1,2) for a value obeying addition mod 2. (The default is regular integer addition).

    Adding or subtracting pairs of QN objects performs addition and subtraction element-wise on each of the named values. If a name is missing from the collection, its value is treated as zero.

    source

    Constructors

    ITensors.QuantumNumbers.QNMethod
    QN(qvs...)

    Construct a QN from a set of up to four named value tuples.

    Examples

    q = QN(("Sz",1))
     q = QN(("N",1),("Sz",-1))
    -q = QN(("P",0,2),("Sz",0)).
    source
    ITensors.QuantumNumbers.QNType
    QN(name,val::Int,modulus::Int=1)

    Construct a QN with a single named value by providing the name, value, and optional modulus.

    source
    ITensors.QuantumNumbers.QNType
    QN(val::Int,modulus::Int=1)

    Construct a QN with a single unnamed value (equivalent to the name being the empty string) with optional modulus.

    source

    Properties

    ITensors.valMethod
    val(q::QN,name)

    Get the value within the QN q corresponding to the string name

    source
    Base.zeroMethod
    zero(q::QN)

    Returns a QN object containing the same names as q, but with all values set to zero.

    source
    +q = QN(("P",0,2),("Sz",0)).
    source
    ITensors.QuantumNumbers.QNType
    QN(name,val::Int,modulus::Int=1)

    Construct a QN with a single named value by providing the name, value, and optional modulus.

    source
    ITensors.QuantumNumbers.QNType
    QN(val::Int,modulus::Int=1)

    Construct a QN with a single unnamed value (equivalent to the name being the empty string) with optional modulus.

    source

    Properties

    ITensors.valMethod
    val(q::QN,name)

    Get the value within the QN q corresponding to the string name

    source
    Base.zeroMethod
    zero(q::QN)

    Returns a QN object containing the same names as q, but with all values set to zero.

    source
    diff --git a/previews/PR1410/QNTricks.html b/previews/PR1410/QNTricks.html index 1f9e4d67ff..271c24c488 100644 --- a/previews/PR1410/QNTricks.html +++ b/previews/PR1410/QNTricks.html @@ -2,71 +2,71 @@ Symmetric (QN conserving) tensors: background and usage · ITensors.jl

    Symmetric (QN Conserving) Tensors: Background and Usage

    Here is a collection of background material and example codes for understanding how symmetric tensors (tensors with conserved quantum numbers) work in ITensors.jl

    Combiners and Symmetric Tensors

    In ITensors.jl, combiners are special sparse tensors that represent the action of taking the tensor product of one or more indices. It generalizes the idea of reshaping and permuting. For dense ITensors, a combiner is just the action of permuting and reshaping the data of the tensor. For symmetric tensors (quantum number conserving tensors represented as block sparse tensors), the combiner also fuses symmetry sectors together. They can be used for various purposes. Generally they are used internally in the library, for example in order to reshape a high order ITensor into an order 2 ITensor to perform a matrix decomposition like an SVD or eigendecomposition.

    For example:

    julia> using ITensors
            
            # This is a short code showing how a combiner
    -       # can be used to "flip" the direction of an Index
    julia> i = Index([QN(0) => 2, QN(1) => 3], "i")(dim=5|id=927|"i") <Out> + # can be used to "flip" the direction of an Index
    julia> i = Index([QN(0) => 2, QN(1) => 3], "i")(dim=5|id=580|"i") <Out> 1: QN(0) => 2 - 2: QN(1) => 3
    julia> j = Index([QN(0) => 2, QN(1) => 3], "j")(dim=5|id=645|"j") <Out> + 2: QN(1) => 3
    julia> j = Index([QN(0) => 2, QN(1) => 3], "j")(dim=5|id=312|"j") <Out> 1: QN(0) => 2 2: QN(1) => 3
    julia> A = randomITensor(i, dag(j))ITensor ord=2 -(dim=5|id=927|"i") <Out> +(dim=5|id=580|"i") <Out> 1: QN(0) => 2 2: QN(1) => 3 -(dim=5|id=645|"j") <In> +(dim=5|id=312|"j") <In> 1: QN(0) => 2 2: QN(1) => 3 NDTensors.BlockSparse{Float64, Vector{Float64}, 2}
    julia> C = combiner(i, dag(j); tags = "c", dir = dir(i))ITensor ord=3 -(dim=25|id=45|"c") <Out> +(dim=25|id=896|"c") <Out> 1: QN(-1) => 6 2: QN(0) => 13 3: QN(1) => 6 -(dim=5|id=927|"i") <In> +(dim=5|id=580|"i") <In> 1: QN(0) => 2 2: QN(1) => 3 -(dim=5|id=645|"j") <Out> +(dim=5|id=312|"j") <Out> 1: QN(0) => 2 2: QN(1) => 3 -NDTensors.Combiner
    julia> inds(A)((dim=5|id=927|"i") <Out> +NDTensors.Combiner
    julia> inds(A)((dim=5|id=580|"i") <Out> 1: QN(0) => 2 - 2: QN(1) => 3, (dim=5|id=645|"j") <In> + 2: QN(1) => 3, (dim=5|id=312|"j") <In> 1: QN(0) => 2 - 2: QN(1) => 3)
    julia> inds(A * C)((dim=25|id=45|"c") <Out> + 2: QN(1) => 3)
    julia> inds(A * C)((dim=25|id=896|"c") <Out> 1: QN(-1) => 6 2: QN(0) => 13 3: QN(1) => 6,)

    You can see that the combiner reshapes the indices of A into a single Index that contains the tensor product of the two input spaces. The spaces have size QN(-1) => 2 * 3, QN(0) => 2 * 2 + 3 * 3, and QN(0) => 2 * 3 (determined from all of the combinations of combining the sectors of the different indices, where the QNs are added and the block dimensions are multiplied). The ordering of the sectors is determined internally by ITensors.jl.

    You can also use a combiner on a single Index, which can be helpful for changing the direction of an Index or combining multiple sectors of the same symmetry into a single sector:

    julia> using ITensors
            
            # This is a short code showing how a combiner
    -       # can be used to "flip" the direction of an Index
    julia> i = Index([QN(0) => 2, QN(1) => 3], "i")(dim=5|id=885|"i") <Out> + # can be used to "flip" the direction of an Index
    julia> i = Index([QN(0) => 2, QN(1) => 3], "i")(dim=5|id=334|"i") <Out> 1: QN(0) => 2 - 2: QN(1) => 3
    julia> j = dag(Index([QN(0) => 2, QN(1) => 3], "j"))(dim=5|id=974|"j") <In> + 2: QN(1) => 3
    julia> j = dag(Index([QN(0) => 2, QN(1) => 3], "j"))(dim=5|id=746|"j") <In> 1: QN(0) => 2 2: QN(1) => 3
    julia> A = randomITensor(i, j)ITensor ord=2 -(dim=5|id=885|"i") <Out> +(dim=5|id=334|"i") <Out> 1: QN(0) => 2 2: QN(1) => 3 -(dim=5|id=974|"j") <In> +(dim=5|id=746|"j") <In> 1: QN(0) => 2 2: QN(1) => 3 NDTensors.BlockSparse{Float64, Vector{Float64}, 2}
    julia> C = combiner(j; tags = "jflip", dir = -dir(j))ITensor ord=2 -(dim=5|id=763|"jflip") <Out> +(dim=5|id=207|"jflip") <Out> 1: QN(-1) => 3 2: QN(0) => 2 -(dim=5|id=974|"j") <Out> +(dim=5|id=746|"j") <Out> 1: QN(0) => 2 2: QN(1) => 3 -NDTensors.Combiner
    julia> inds(A)((dim=5|id=885|"i") <Out> +NDTensors.Combiner
    julia> inds(A)((dim=5|id=334|"i") <Out> 1: QN(0) => 2 - 2: QN(1) => 3, (dim=5|id=974|"j") <In> + 2: QN(1) => 3, (dim=5|id=746|"j") <In> 1: QN(0) => 2 - 2: QN(1) => 3)
    julia> inds(A * C)((dim=5|id=763|"jflip") <Out> + 2: QN(1) => 3)
    julia> inds(A * C)((dim=5|id=207|"jflip") <Out> 1: QN(-1) => 3 - 2: QN(0) => 2, (dim=5|id=885|"i") <Out> + 2: QN(0) => 2, (dim=5|id=334|"i") <Out> 1: QN(0) => 2 - 2: QN(1) => 3)

    Unless you are writing very specialized custom code with symmetric tensors, this is generally not needed.

    Block Sparsity and Quantum Numbers

    In general, not all blocks that are allowed according to the flux will actually exist in the tensor (which helps in many cases for efficiency). Usually this would happen when the tensor is first constructed and not all blocks are explicitly set:

    julia> using ITensors
    julia> i = Index([QN(0) => 1, QN(1) => 1])(dim=2|id=14) <Out> + 2: QN(1) => 3)

    Unless you are writing very specialized custom code with symmetric tensors, this is generally not needed.

    Block Sparsity and Quantum Numbers

    In general, not all blocks that are allowed according to the flux will actually exist in the tensor (which helps in many cases for efficiency). Usually this would happen when the tensor is first constructed and not all blocks are explicitly set:

    julia> using ITensors
    julia> i = Index([QN(0) => 1, QN(1) => 1])(dim=2|id=575) <Out> 1: QN(0) => 1 2: QN(1) => 1
    julia> A = ITensor(i', dag(i));
    julia> A[2, 2] = 1.0;
    julia> @show A;A = ITensor ord=2 -Dim 1: (dim=2|id=14)' <Out> +Dim 1: (dim=2|id=575)' <Out> 1: QN(0) => 1 2: QN(1) => 1 -Dim 2: (dim=2|id=14) <In> +Dim 2: (dim=2|id=575) <In> 1: QN(0) => 1 2: QN(1) => 1 NDTensors.BlockSparse{Float64, Vector{Float64}, 2} @@ -74,31 +74,31 @@ Block(2, 2) [2:2, 2:2] 1.0
    julia> D, U = eigen(A; ishermitian=true);
    julia> @show D;D = ITensor ord=2 -Dim 1: (dim=1|id=532|"Link,eigen")' <Out> +Dim 1: (dim=1|id=507|"Link,eigen")' <Out> 1: QN(1) => 1 -Dim 2: (dim=1|id=532|"Link,eigen") <In> +Dim 2: (dim=1|id=507|"Link,eigen") <In> 1: QN(1) => 1 NDTensors.DiagBlockSparse{Float64, Vector{Float64}, 2} 1×1 Block(1, 1) [1:1, 1:1] 1.0
    julia> @show U;U = ITensor ord=2 -Dim 1: (dim=2|id=14) <Out> +Dim 1: (dim=2|id=575) <Out> 1: QN(0) => 1 2: QN(1) => 1 -Dim 2: (dim=1|id=532|"Link,eigen") <In> +Dim 2: (dim=1|id=507|"Link,eigen") <In> 1: QN(1) => 1 NDTensors.BlockSparse{Float64, Vector{Float64}, 2} 2×1 Block(2, 1) [2:2, 1:1] - 1.0

    If we had set A[1, 1] = 0.0 as well, then all of the allowed blocks (according to the flux QN(0) would exist and would be included in the eigendecomposition:

    julia> using ITensors
    julia> i = Index([QN(0) => 1, QN(1) => 1])(dim=2|id=846) <Out> + 1.0

    If we had set A[1, 1] = 0.0 as well, then all of the allowed blocks (according to the flux QN(0) would exist and would be included in the eigendecomposition:

    julia> using ITensors
    julia> i = Index([QN(0) => 1, QN(1) => 1])(dim=2|id=461) <Out> 1: QN(0) => 1 2: QN(1) => 1
    julia> A = ITensor(i', dag(i));
    julia> A[2, 2] = 1.0;
    julia> A[1, 1] = 0.0;
    julia> @show A;A = ITensor ord=2 -Dim 1: (dim=2|id=846)' <Out> +Dim 1: (dim=2|id=461)' <Out> 1: QN(0) => 1 2: QN(1) => 1 -Dim 2: (dim=2|id=846) <In> +Dim 2: (dim=2|id=461) <In> 1: QN(0) => 1 2: QN(1) => 1 NDTensors.BlockSparse{Float64, Vector{Float64}, 2} @@ -110,10 +110,10 @@ Block(1, 1) [1:1, 1:1] 0.0
    julia> D, U = eigen(A; ishermitian=true);
    julia> @show D;D = ITensor ord=2 -Dim 1: (dim=2|id=816|"Link,eigen")' <Out> +Dim 1: (dim=2|id=263|"Link,eigen")' <Out> 1: QN(0) => 1 2: QN(1) => 1 -Dim 2: (dim=2|id=816|"Link,eigen") <In> +Dim 2: (dim=2|id=263|"Link,eigen") <In> 1: QN(0) => 1 2: QN(1) => 1 NDTensors.DiagBlockSparse{Float64, Vector{Float64}, 2} @@ -125,10 +125,10 @@ Block(2, 2) [2:2, 2:2] 1.0
    julia> @show U;U = ITensor ord=2 -Dim 1: (dim=2|id=846) <Out> +Dim 1: (dim=2|id=461) <Out> 1: QN(0) => 1 2: QN(1) => 1 -Dim 2: (dim=2|id=816|"Link,eigen") <In> +Dim 2: (dim=2|id=263|"Link,eigen") <In> 1: QN(0) => 1 2: QN(1) => 1 NDTensors.BlockSparse{Float64, Vector{Float64}, 2} @@ -139,24 +139,24 @@ Block(2, 2) [2:2, 2:2] - 1.0

    "Missing" blocks can also occur with tensor contractions, since the final blocks of the output tensor are made from combinations of contractions of blocks from the input tensors, and there is no guarantee that all flux-consistent blocks will end up in the result:

    julia> using ITensors
    julia> i = Index([QN(0) => 1, QN(1) => 1])(dim=2|id=630) <Out> + 1.0

    "Missing" blocks can also occur with tensor contractions, since the final blocks of the output tensor are made from combinations of contractions of blocks from the input tensors, and there is no guarantee that all flux-consistent blocks will end up in the result:

    julia> using ITensors
    julia> i = Index([QN(0) => 1, QN(1) => 1])(dim=2|id=844) <Out> 1: QN(0) => 1 - 2: QN(1) => 1
    julia> j = Index([QN(0) => 1])(dim=1|id=475) <Out> + 2: QN(1) => 1
    julia> j = Index([QN(0) => 1])(dim=1|id=332) <Out> 1: QN(0) => 1
    julia> A = ITensor(i, dag(j));
    julia> A[2, 1] = 1.0;
    julia> @show A;A = ITensor ord=2 -Dim 1: (dim=2|id=630) <Out> +Dim 1: (dim=2|id=844) <Out> 1: QN(0) => 1 2: QN(1) => 1 -Dim 2: (dim=1|id=475) <In> +Dim 2: (dim=1|id=332) <In> 1: QN(0) => 1 NDTensors.BlockSparse{Float64, Vector{Float64}, 2} 2×1 Block(2, 1) [2:2, 1:1] 1.0
    julia> A2 = prime(A, i) * dag(A);
    julia> @show A2;A2 = ITensor ord=2 -Dim 1: (dim=2|id=630)' <Out> +Dim 1: (dim=2|id=844)' <Out> 1: QN(0) => 1 2: QN(1) => 1 -Dim 2: (dim=2|id=630) <In> +Dim 2: (dim=2|id=844) <In> 1: QN(0) => 1 2: QN(1) => 1 NDTensors.BlockSparse{Float64, Vector{Float64}, 2} @@ -164,22 +164,22 @@ Block(2, 2) [2:2, 2:2] 1.0
    julia> D, U = eigen(A2; ishermitian=true);
    julia> @show D;D = ITensor ord=2 -Dim 1: (dim=1|id=380|"Link,eigen")' <Out> +Dim 1: (dim=1|id=102|"Link,eigen")' <Out> 1: QN(1) => 1 -Dim 2: (dim=1|id=380|"Link,eigen") <In> +Dim 2: (dim=1|id=102|"Link,eigen") <In> 1: QN(1) => 1 NDTensors.DiagBlockSparse{Float64, Vector{Float64}, 2} 1×1 Block(1, 1) [1:1, 1:1] 1.0
    julia> @show U;U = ITensor ord=2 -Dim 1: (dim=2|id=630) <Out> +Dim 1: (dim=2|id=844) <Out> 1: QN(0) => 1 2: QN(1) => 1 -Dim 2: (dim=1|id=380|"Link,eigen") <In> +Dim 2: (dim=1|id=102|"Link,eigen") <In> 1: QN(1) => 1 NDTensors.BlockSparse{Float64, Vector{Float64}, 2} 2×1 Block(2, 1) [2:2, 1:1] - 1.0
    + 1.0 diff --git a/previews/PR1410/RunningOnGPUs.html b/previews/PR1410/RunningOnGPUs.html index fa0fa73c1b..08c24787ae 100644 --- a/previews/PR1410/RunningOnGPUs.html +++ b/previews/PR1410/RunningOnGPUs.html @@ -26,4 +26,4 @@ Bmtl = mtl(B) # Perform tensor operations on Apple GPU -Amtl * Bmtl

    Note that we highly recommend using these new package extensions as opposed to ITensorGPU.jl, which is ITensor's previous CUDA backend. The package extensions are better integrated into the main library so are more reliable and better supported right now. We plan to deprecate ITensorGPU.jl in the future.

    GPU backends

    ITensor currently provides package extensions for the following GPU backends:

    Our goal is to support all GPU backends which are supported by the JuliaGPU organization.

    Some important caveats to keep in mind related to the ITensor GPU backends are:

    The table below summarizes each backend's current capabilities.

    CUDAROCmMetaloneAPI
    Contractions (dense)N/A
    Contractions (cuTENSOR)In progressN/AN/AN/A
    QR (dense)On CPUOn CPUN/A
    SVD (dense)On CPUOn CPUN/A
    Eigendecomposition (dense)On CPUOn CPUN/A
    Double precision (Float64)N/AN/A
    Block sparseIn progressIn progressIn progressN/A
    +Amtl * Bmtl

    Note that we highly recommend using these new package extensions as opposed to ITensorGPU.jl, which is ITensor's previous CUDA backend. The package extensions are better integrated into the main library so are more reliable and better supported right now. We plan to deprecate ITensorGPU.jl in the future.

    GPU backends

    ITensor currently provides package extensions for the following GPU backends:

    Our goal is to support all GPU backends which are supported by the JuliaGPU organization.

    Notice that cuTENSOR.jl is an extension of CUDA.jl that provides new functionality for accelerated binary tensor contractions. If the cuTENSOR.jl library is loaded then ITensors with CuArray data are contracted using cuTENSOR and if the cuTENSOR.jl library is not loaded but CUDA.jl is loaded then binary tensor contractions are mapped to a matrix multiplication and performed using cuBLAS.

    Some important caveats to keep in mind related to the ITensor GPU backends are:

    The table below summarizes each backend's current capabilities.

    CUDAcuTENSORROCmMetaloneAPI
    Contractions (dense)✓ (cuBLAS)N/A
    QR (dense)✓ (cuSOLVER)✓ (cuSOLVER)On CPUOn CPUN/A
    SVD (dense)✓ (cuSOLVER)✓ (cuSOLVER)On CPUOn CPUN/A
    Eigendecomposition (dense)✓ (cuSOLVER)✓ (cuSOLVER)On CPUOn CPUN/A
    Double precision (Float64)✓ (cuSOLVER)N/AN/A
    Block sparseIn progressIn progressIn progressIn progressN/A
    diff --git a/previews/PR1410/SiteType.html b/previews/PR1410/SiteType.html index 717f75b4a1..f925445254 100644 --- a/previews/PR1410/SiteType.html +++ b/previews/PR1410/SiteType.html @@ -41,8 +41,8 @@ 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 - 0.0 0.0 0.0 1.0

    Many operators are available, for example:

    You can view the source code for the internal SiteType definitions and operators that are defined here.

    source

    Methods

    ITensors.SiteTypes.opFunction
    op(opname::String, s::Index; kwargs...)

    Return an ITensor corresponding to the operator named opname for the Index s. The operator is constructed by calling an overload of either the op or op! methods which take a SiteType argument that corresponds to one of the tags of the Index s and an OpName"opname" argument that corresponds to the input operator name.

    Operator names can be combined using the "*" symbol, for example "S+*S-" or "Sz*Sz*Sz". The result is an ITensor made by forming each operator then contracting them together in a way corresponding to the usual operator product or matrix multiplication.

    The op system is used by the OpSum system to convert operator names into ITensors, and can be used directly such as for applying operators to MPS.

    Example

    s = Index(2, "Site,S=1/2")
    -Sz = op("Sz", s)

    To see all of the operator names defined for the site types included with ITensor, please view the source code for each site type. Note that some site types such as "S=1/2" and "Qubit" are aliases for each other and share operator definitions.

    source
    op(X::AbstractArray, s::Index...)
    + 0.0  0.0  0.0  1.0

    Many operators are available, for example:

    • SiteType"S=1/2": "Sz", "Sx", "Sy", "S+", "S-", ...
    • SiteType"Electron": "Nup", "Ndn", "Nupdn", "Ntot", "Cup", "Cdagup", "Cdn", "Cdagdn", "Sz", "Sx", "Sy", "S+", "S-", ...
    • ...

    You can view the source code for the internal SiteType definitions and operators that are defined here.

    source

    Methods

    ITensors.SiteTypes.opFunction
    op(opname::String, s::Index; kwargs...)

    Return an ITensor corresponding to the operator named opname for the Index s. The operator is constructed by calling an overload of either the op or op! methods which take a SiteType argument that corresponds to one of the tags of the Index s and an OpName"opname" argument that corresponds to the input operator name.

    Operator names can be combined using the "*" symbol, for example "S+*S-" or "Sz*Sz*Sz". The result is an ITensor made by forming each operator then contracting them together in a way corresponding to the usual operator product or matrix multiplication.

    The op system is used by the OpSum system to convert operator names into ITensors, and can be used directly such as for applying operators to MPS.

    Example

    s = Index(2, "Site,S=1/2")
    +Sz = op("Sz", s)

    To see all of the operator names defined for the site types included with ITensor, please view the source code for each site type. Note that some site types such as "S=1/2" and "Qubit" are aliases for each other and share operator definitions.

    source
    op(X::AbstractArray, s::Index...)
     op(M::Matrix, s::Index...)

    Given a matrix M and a set of indices s,t,... return an operator ITensor with matrix elements given by M and indices s, s', t, t'

    Example

    julia> s = siteind("S=1/2")
     (dim=2|id=575|"S=1/2,Site")
     
    @@ -59,57 +59,57 @@
      0.5   0.0
      0.0  -0.5
     ITensor ord=2 (dim=2|id=575|"S=1/2,Site")' (dim=2|id=575|"S=1/2,Site")
    -NDTensors.Dense{Float64, Vector{Float64}}
    source
    op(opname::String,sites::Vector{<:Index},n::Int; kwargs...)

    Return an ITensor corresponding to the operator named opname for the n'th Index in the array sites.

    Example

    s = siteinds("S=1/2", 4)
    -Sz2 = op("Sz", s, 2)
    source
    ITensors.SiteTypes.stateFunction
    state(s::Index, name::String; kwargs...)

    Return an ITensor corresponding to the state named name for the Index s. The returned ITensor will have s as its only index.

    The terminology here is based on the idea of a single-site state or wavefunction in physics.

    The state function is implemented for various Index tags by overloading either the state or state! methods which take a SiteType argument corresponding to one of the tags of the Index s and an StateName"name" argument that corresponds to the input state name.

    The state system is used by the MPS type to construct product-state MPS and for other purposes.

    Example

    s = Index(2, "Site,S=1/2")
    +NDTensors.Dense{Float64, Vector{Float64}}
    source
    op(opname::String,sites::Vector{<:Index},n::Int; kwargs...)

    Return an ITensor corresponding to the operator named opname for the n'th Index in the array sites.

    Example

    s = siteinds("S=1/2", 4)
    +Sz2 = op("Sz", s, 2)
    source
    ITensors.SiteTypes.stateFunction
    state(s::Index, name::String; kwargs...)

    Return an ITensor corresponding to the state named name for the Index s. The returned ITensor will have s as its only index.

    The terminology here is based on the idea of a single-site state or wavefunction in physics.

    The state function is implemented for various Index tags by overloading either the state or state! methods which take a SiteType argument corresponding to one of the tags of the Index s and an StateName"name" argument that corresponds to the input state name.

    The state system is used by the MPS type to construct product-state MPS and for other purposes.

    Example

    s = Index(2, "Site,S=1/2")
     sup = state(s,"Up")
     sdn = state(s,"Dn")
     sxp = state(s,"X+")
    -sxm = state(s,"X-")
    source
    ITensors.valFunction
    val(q::QN,name)

    Get the value within the QN q corresponding to the string name

    source
    val(s::Index, name::String)

    Return an integer corresponding to the name of a certain value the Index s can take. In other words, the val function maps strings to specific integer values within the range 1:dim(s).

    The val function is implemented for various Index tags by overloading methods named val which take a SiteType argument corresponding to one of the tags of the Index s and an ValName"name" argument that corresponds to the input name.

    Example

    s = Index(2, "Site,S=1/2")
    +sxm = state(s,"X-")
    source
    ITensors.valFunction
    val(q::QN,name)

    Get the value within the QN q corresponding to the string name

    source
    val(s::Index, name::String)

    Return an integer corresponding to the name of a certain value the Index s can take. In other words, the val function maps strings to specific integer values within the range 1:dim(s).

    The val function is implemented for various Index tags by overloading methods named val which take a SiteType argument corresponding to one of the tags of the Index s and an ValName"name" argument that corresponds to the input name.

    Example

    s = Index(2, "Site,S=1/2")
     val(s,"Up") == 1
     val(s,"Dn") == 2
     
     s = Index(2, "Site,Fermion")
     val(s,"Emp") == 1
    -val(s,"Occ") == 2
    source
    ITensors.spaceFunction
    space(::SiteType"Qubit";
    +val(s,"Occ") == 2
    source
    ITensors.spaceFunction
    space(::SiteType"Qubit";
           conserve_qns = false,
           conserve_parity = conserve_qns,
           conserve_number = false,
           qnname_parity = "Parity",
    -      qnname_number = "Number")

    Create the Hilbert space for a site of type "Qubit".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    space(::SiteType"S=1/2";
    +      qnname_number = "Number")

    Create the Hilbert space for a site of type "Qubit".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    space(::SiteType"S=1/2";
           conserve_qns = false,
           conserve_sz = conserve_qns,
           conserve_szparity = false,
           qnname_sz = "Sz",
    -      qnname_szparity = "SzParity")

    Create the Hilbert space for a site of type "S=1/2".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    space(::SiteType"S=1";
    +      qnname_szparity = "SzParity")

    Create the Hilbert space for a site of type "S=1/2".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    space(::SiteType"S=1";
           conserve_qns = false,
           conserve_sz = conserve_qns,
    -      qnname_sz = "Sz")

    Create the Hilbert space for a site of type "S=1".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    space(::SiteType"Fermion";
    +      qnname_sz = "Sz")

    Create the Hilbert space for a site of type "S=1".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    space(::SiteType"Fermion";
           conserve_qns=false,
           conserve_nf=conserve_qns,
           conserve_nfparity=conserve_qns,
           qnname_nf = "Nf",
           qnname_nfparity = "NfParity",
           qnname_sz = "Sz",
    -      conserve_sz = false)

    Create the Hilbert space for a site of type "Fermion".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    space(::SiteType"Electron";
    +      conserve_sz = false)

    Create the Hilbert space for a site of type "Fermion".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    space(::SiteType"Electron";
           conserve_qns = false,
           conserve_sz = conserve_qns,
           conserve_nf = conserve_qns,
           conserve_nfparity = conserve_qns,
           qnname_sz = "Sz",
           qnname_nf = "Nf",
    -      qnname_nfparity = "NfParity")

    Create the Hilbert space for a site of type "Electron".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    space(::SiteType"tJ";
    +      qnname_nfparity = "NfParity")

    Create the Hilbert space for a site of type "Electron".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    space(::SiteType"tJ";
           conserve_qns = false,
           conserve_sz = conserve_qns,
           conserve_nf = conserve_qns,
           conserve_nfparity = conserve_qns,
           qnname_sz = "Sz",
           qnname_nf = "Nf",
    -      qnname_nfparity = "NfParity")

    Create the Hilbert space for a site of type "tJ".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    space(::SiteType"Qudit";
    +      qnname_nfparity = "NfParity")

    Create the Hilbert space for a site of type "tJ".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    space(::SiteType"Qudit";
           dim = 2,
           conserve_qns = false,
           conserve_number = false,
    -      qnname_number = "Number")

    Create the Hilbert space for a site of type "Qudit".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    space(::SiteType"Boson";
    +      qnname_number = "Number")

    Create the Hilbert space for a site of type "Qudit".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    space(::SiteType"Boson";
           dim = 2,
           conserve_qns = false,
           conserve_number = false,
    -      qnname_number = "Number")

    Create the Hilbert space for a site of type "Boson".

    Optionally specify the conserved symmetries and their quantum number labels.

    source
    + qnname_number = "Number")

    Create the Hilbert space for a site of type "Boson".

    Optionally specify the conserved symmetries and their quantum number labels.

    source diff --git a/previews/PR1410/Sweeps.html b/previews/PR1410/Sweeps.html index 8850359b2c..20e8e90426 100644 --- a/previews/PR1410/Sweeps.html +++ b/previews/PR1410/Sweeps.html @@ -1,5 +1,5 @@ -Sweeps · ITensors.jl

    Sweeps

    ITensors.ITensorMPS.SweepsType

    A Sweeps objects holds information about the various parameters controlling a density matrix renormalization group (DMRG) or similar matrix product state (MPS) calculation.

    For a Sweeps object sw the available parameters are:

    • nsweep(sw) – the number of sweeps to do
    • maxdim(sw,n) – maximum MPS bond dimension for sweep n
    • mindim(sw,n) – minimum MPS bond dimension for sweep n
    • cutoff(sw,n) – truncation error cutoff for sweep n
    • noise(sw,n) – noise term coefficient for sweep n
    source
    ITensors.ITensorMPS.SweepsMethod
    Sweeps(d::AbstractMatrix)
    +Sweeps · ITensors.jl

    Sweeps

    ITensors.ITensorMPS.SweepsType

    A Sweeps objects holds information about the various parameters controlling a density matrix renormalization group (DMRG) or similar matrix product state (MPS) calculation.

    For a Sweeps object sw the available parameters are:

    • nsweep(sw) – the number of sweeps to do
    • maxdim(sw,n) – maximum MPS bond dimension for sweep n
    • mindim(sw,n) – minimum MPS bond dimension for sweep n
    • cutoff(sw,n) – truncation error cutoff for sweep n
    • noise(sw,n) – noise term coefficient for sweep n
    source
    ITensors.ITensorMPS.SweepsMethod
    Sweeps(d::AbstractMatrix)
     
     Sweeps(nsweep::Int, d::AbstractMatrix)

    Make a sweeps object from a matrix of input values. The first row should be strings that define which variables are being set ("maxdim", "cutoff", "mindim", and "noise").

    If the number of sweeps are not specified, they are determined from the size of the input matrix.

    Examples

    julia > Sweeps(
       [
    @@ -18,5 +18,5 @@
     3cutoff = 1.0E-12, maxdim = 200, mindim = 20, noise = 1.0E-10
     4cutoff = 1.0E-12, maxdim = 400, mindim = 20, noise = 0.0E+00
     5cutoff = 1.0E-12, maxdim = 800, mindim = 20, noise = 1.0E-11
    -6cutoff = 1.0E-12, maxdim = 800, mindim = 20, noise = 0.0E+00
    source

    Modifying Sweeps Objects

    ITensors.ITensorMPS.setmaxdim!Function
    maxdim!(sw::Sweeps,maxdims::Int...)

    Set the maximum MPS bond dimension for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.

    source
    ITensors.ITensorMPS.setcutoff!Function
    cutoff!(sw::Sweeps,maxdims::Int...)

    Set the MPS truncation error used for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.

    source
    ITensors.ITensorMPS.setnoise!Function
    noise!(sw::Sweeps,maxdims::Int...)

    Set the noise-term coefficient used for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.

    source
    ITensors.ITensorMPS.setmindim!Function
    mindim!(sw::Sweeps,maxdims::Int...)

    Set the minimum MPS bond dimension for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.

    source

    Getting Sweeps Object Data

    NDTensors.maxdimMethod
    maxdim(sw::Sweeps,n::Int)

    Maximum MPS bond dimension allowed by the Sweeps object sw during sweep n

    source
    NDTensors.mindimMethod
    mindim(sw::Sweeps,n::Int)

    Minimum MPS bond dimension allowed by the Sweeps object sw during sweep n

    source
    +6cutoff = 1.0E-12, maxdim = 800, mindim = 20, noise = 0.0E+00
    source

    Modifying Sweeps Objects

    ITensors.ITensorMPS.setmaxdim!Function
    maxdim!(sw::Sweeps,maxdims::Int...)

    Set the maximum MPS bond dimension for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.

    source
    ITensors.ITensorMPS.setcutoff!Function
    cutoff!(sw::Sweeps,maxdims::Int...)

    Set the MPS truncation error used for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.

    source
    ITensors.ITensorMPS.setnoise!Function
    noise!(sw::Sweeps,maxdims::Int...)

    Set the noise-term coefficient used for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.

    source
    ITensors.ITensorMPS.setmindim!Function
    mindim!(sw::Sweeps,maxdims::Int...)

    Set the minimum MPS bond dimension for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.

    source

    Getting Sweeps Object Data

    NDTensors.maxdimMethod
    maxdim(sw::Sweeps,n::Int)

    Maximum MPS bond dimension allowed by the Sweeps object sw during sweep n

    source
    NDTensors.mindimMethod
    mindim(sw::Sweeps,n::Int)

    Minimum MPS bond dimension allowed by the Sweeps object sw during sweep n

    source
    diff --git a/previews/PR1410/UpgradeGuide_0.1_to_0.2.html b/previews/PR1410/UpgradeGuide_0.1_to_0.2.html index edd98aaee7..aa6ed5599f 100644 --- a/previews/PR1410/UpgradeGuide_0.1_to_0.2.html +++ b/previews/PR1410/UpgradeGuide_0.1_to_0.2.html @@ -185,4 +185,4 @@ 2: QN("Sz",-1) => 1 (dim=2|id=810|"S=1/2,Site,n=4") <Out> 1: QN("Sz",1) => 1 - 2: QN("Sz",-1) => 1

    This shouldn't affect end users in general. The new convention is a bit more intuitive since the quantum number can be thought of as counting the total number of 1 bits in the state, though the conventions can be mapped to each other with a constant.

    maxlinkdim for MPS/MPO with no indices

    maxlinkdim(::MPS/MPO) returns a minimum of 1 (previously it returned 0 for MPS/MPO without and link indices) (PR #663).

    + 2: QN("Sz",-1) => 1

    This shouldn't affect end users in general. The new convention is a bit more intuitive since the quantum number can be thought of as counting the total number of 1 bits in the state, though the conventions can be mapped to each other with a constant.

    maxlinkdim for MPS/MPO with no indices

    maxlinkdim(::MPS/MPO) returns a minimum of 1 (previously it returned 0 for MPS/MPO without and link indices) (PR #663).

    diff --git a/previews/PR1410/examples/DMRG.html b/previews/PR1410/examples/DMRG.html index 229915c774..46ecb40a62 100644 --- a/previews/PR1410/examples/DMRG.html +++ b/previews/PR1410/examples/DMRG.html @@ -310,4 +310,4 @@ After sweep 4, |psi| = 2.863 MiB, |PH| = 7.246 MiB After sweep 4 energy=-44.127710946536645 maxlinkdim=56 maxerr=9.99E-09 time=0.445 After sweep 5, |psi| = 3.108 MiB, |PH| = 7.845 MiB -After sweep 5 energy=-44.127736798226536 maxlinkdim=57 maxerr=9.98E-09 time=0.564 +After sweep 5 energy=-44.127736798226536 maxlinkdim=57 maxerr=9.98E-09 time=0.564 diff --git a/previews/PR1410/examples/ITensor.html b/previews/PR1410/examples/ITensor.html index 57d02a2055..4ecc504fd1 100644 --- a/previews/PR1410/examples/ITensor.html +++ b/previews/PR1410/examples/ITensor.html @@ -23,8 +23,8 @@ T = randomITensor(k,m) @show T
    T = ITensor ord=2
    -Dim 1: (dim=4|id=9|"index_k")
    -Dim 2: (dim=2|id=415|"index_m")
    +Dim 1: (dim=4|id=556|"index_k")
    +Dim 2: (dim=2|id=505|"index_m")
     NDTensors.Dense{Float64, Vector{Float64}}
      4×2
      -0.037025446544394686   0.4967800795298839
    @@ -57,21 +57,21 @@
     T .= myf.(T)

    Making an ITensor with a Single Non-Zero Element

    It is often useful to make ITensors with all elements zero except for a specific element that is equal to 1.0. Use cases can include making product-state quantum wavefunctions or contracting single-element ITensors with other ITensors to set their indices to a fixed value.

    To make such an ITensor, use the onehot function. Borrowing terminology from engineering, a "one hot" vector or tensor has a single element equal to 1.0 and the rest zero. (In previous versions of ITensor this function was called setelt.)

    The ITensor function onehot takes one or more Index-value Pairs such as i=>2 and j=>1 and returns an ITensor with a 1.0 in the location specified by the Index values:

    i = Index(2)
     O1 = onehot(i=>1)
     println(O1)
    ITensor ord=1
    -Dim 1: (dim=2|id=108)
    +Dim 1: (dim=2|id=40)
     NDTensors.Dense{Float64, Vector{Float64}}
      2-element
      1.0
      0.0
    O2 = onehot(i=>2)
     println(O2)
    ITensor ord=1
    -Dim 1: (dim=2|id=677)
    +Dim 1: (dim=2|id=745)
     NDTensors.Dense{Float64, Vector{Float64}}
      2-element
      0.0
      1.0
    j = Index(3)
     T = onehot(i=>2,j=>3)
     println(T)
    ITensor ord=2
    -Dim 1: (dim=2|id=426)
    -Dim 2: (dim=3|id=193)
    +Dim 1: (dim=2|id=933)
    +Dim 2: (dim=3|id=881)
     NDTensors.Dense{Float64, Vector{Float64}}
      2×3
      0.0  0.0  0.0
    @@ -105,11 +105,11 @@
     @show norm(U*S*V-T)
     @show (norm(U*S*V - T)/norm(T))^2

    QR Factorization

    Computing the QR factorization of an ITensor works in a similar way as for the SVD. In addition to passing the ITensor you want to factorize, you must also pass the indices you want to end up on the tensor Q, in other words to be treated as the "row" indices for the purpose of defining the QR factorization.

    Say we want to compute the QR factorization of an ITensor T with indices i,j,k, putting the indices i and k onto Q and the remaining indices onto R. We can do this as follows:

    T = randomITensor(i,j,k)
     Q,R = qr(T,(i,k);positive=true)

    Note the use of the optional positive=true keyword argument, which ensures that the diagonal elements of R are non-negative. With this option, the QR factorization is unique, which can be useful in certain cases.

    Combining Multiple Indices into One Index

    It can be very useful to combine or merge multiple indices of an ITensor into a single Index. Say we have an ITensor with indices i,j,k and we want to combine Index i and Index k into a new Index. This new Index (call it c) will have a dimension whose size is the dimension of i times the dimension of k.

    To carry out this procedure we can make a special kind of ITensor: a combiner. To make a combiner, call the function combiner, passing the indices you want to combine:

    C = combiner(i,k; tags="c")

    Then if we have an ITensor

    T = randomITensor(i,j,k)
    -@show inds(T)
    ((dim=4|id=605|"i"), (dim=3|id=885|"j"), (dim=2|id=316|"k"))

    we can combine indices i and k by contracting with the combiner:

    CT = C * T

    Printing out the indices of the new ITensor CT we can see that it has only two indices:

    @show inds(CT)
    ((dim=8|id=163|"c"), (dim=3|id=885|"j"))

    The first is the newly made combined Index, which was made for us by the combiner function and the second is the j Index of T which was not part of the combining process. To access the combined Index you can call the combinedind function on the combiner:

    ci = combinedind(C)
    (dim=8|id=163|"c")

    We can visualize all of the steps above as follows:

    Combining is not limited to two indices and you can combine any number of indices, in any order, using a combiner.

    To undo the combining process and uncombine the Index c back into i,k, just contract with the conjugate of the combiner ITensor dag(C).

    UT = dag(C) * CT
    -@show inds(UT)
    ((dim=4|id=605|"i"), (dim=2|id=316|"k"), (dim=3|id=885|"j"))

    Write and Read an ITensor to Disk with HDF5

    Info

    Make sure to install the HDF5 package to use this feature. (Run julia> ] add HDF5 in the Julia REPL console.)

    Saving ITensors to disk can be very useful. For example, you might encounter a bug in your own code, and by reading the ITensors involved from disk you can shortcut the process of running a lengthy algorithm over many times to reproduce the bug. Or you can save the output of an expensive calculation, such as a DMRG calculation, and use it as a starting point for multiple follow-up calculations such as computing time-dependent properties.

    ITensors can be written to files using the HDF5 format. HDF5 offers many benefits such as being portable across different machine types, and offers a standard interface across various libraries and languages.

    Writing an ITensor to an HDF5 File

    Let's say you have an ITensor T which you have made or obtained from a calculation. To write it to an HDF5 file named "myfile.h5" you can use the following pattern:

    using HDF5
    +@show inds(T)
    ((dim=4|id=783|"i"), (dim=3|id=970|"j"), (dim=2|id=647|"k"))

    we can combine indices i and k by contracting with the combiner:

    CT = C * T

    Printing out the indices of the new ITensor CT we can see that it has only two indices:

    @show inds(CT)
    ((dim=8|id=291|"c"), (dim=3|id=970|"j"))

    The first is the newly made combined Index, which was made for us by the combiner function and the second is the j Index of T which was not part of the combining process. To access the combined Index you can call the combinedind function on the combiner:

    ci = combinedind(C)
    (dim=8|id=291|"c")

    We can visualize all of the steps above as follows:

    Combining is not limited to two indices and you can combine any number of indices, in any order, using a combiner.

    To undo the combining process and uncombine the Index c back into i,k, just contract with the conjugate of the combiner ITensor dag(C).

    UT = dag(C) * CT
    +@show inds(UT)
    ((dim=4|id=783|"i"), (dim=2|id=647|"k"), (dim=3|id=970|"j"))

    Write and Read an ITensor to Disk with HDF5

    Info

    Make sure to install the HDF5 package to use this feature. (Run julia> ] add HDF5 in the Julia REPL console.)

    Saving ITensors to disk can be very useful. For example, you might encounter a bug in your own code, and by reading the ITensors involved from disk you can shortcut the process of running a lengthy algorithm over many times to reproduce the bug. Or you can save the output of an expensive calculation, such as a DMRG calculation, and use it as a starting point for multiple follow-up calculations such as computing time-dependent properties.

    ITensors can be written to files using the HDF5 format. HDF5 offers many benefits such as being portable across different machine types, and offers a standard interface across various libraries and languages.

    Writing an ITensor to an HDF5 File

    Let's say you have an ITensor T which you have made or obtained from a calculation. To write it to an HDF5 file named "myfile.h5" you can use the following pattern:

    using HDF5
     f = h5open("myfile.h5","w")
     write(f,"T",T)
     close(f)

    Above, the string "T" can actually be any string you want such as "ITensor T" or "Result Tensor" and doesn't have to have the same name as the reference T. Closing the file f is optional and you can also write other objects to the same file before closing it.

    Reading an ITensor from an HDF5 File

    Say you have an HDF5 file "myfile.h5" which contains an ITensor stored as a dataset with the name "T". (Which would be the situation if you wrote it as in the example above.) To read this ITensor back from the HDF5 file, use the following pattern:

    using HDF5
     f = h5open("myfile.h5","r")
     T = read(f,"T",ITensor)
    -close(f)

    Note the ITensor argument to the read function, which tells Julia which read function to call and how to interpret the data stored in the HDF5 dataset named "T". In the future we might lift the requirement of providing the type and have it be detected automatically from the data stored in the file.

    +close(f)

    Note the ITensor argument to the read function, which tells Julia which read function to call and how to interpret the data stored in the HDF5 dataset named "T". In the future we might lift the requirement of providing the type and have it be detected automatically from the data stored in the file.

    diff --git a/previews/PR1410/examples/MPSandMPO.html b/previews/PR1410/examples/MPSandMPO.html index c77b5d2fbd..cce23abf87 100644 --- a/previews/PR1410/examples/MPSandMPO.html +++ b/previews/PR1410/examples/MPSandMPO.html @@ -81,4 +81,4 @@ H = MPO(os,sites) # Compute <psi|H|psi> -energy_psi = inner(psi',H,psi)

    Note the MPS argument to the read function, which tells Julia which read function to call and how to interpret the data stored in the HDF5 dataset named "psi". In the future we might lift the requirement of providing the type and have it be detected automatically from the data stored in the file.

    Writing and Reading MPOs

    To write or read MPOs to or from HDF5 files, just follow the examples above but use the type MPO when reading an MPO from the file instead of the type MPS.

    +energy_psi = inner(psi',H,psi)

    Note the MPS argument to the read function, which tells Julia which read function to call and how to interpret the data stored in the HDF5 dataset named "psi". In the future we might lift the requirement of providing the type and have it be detected automatically from the data stored in the file.

    Writing and Reading MPOs

    To write or read MPOs to or from HDF5 files, just follow the examples above but use the type MPO when reading an MPO from the file instead of the type MPS.

    diff --git a/previews/PR1410/examples/Physics.html b/previews/PR1410/examples/Physics.html index 97af0bdc2a..a37868bf65 100644 --- a/previews/PR1410/examples/Physics.html +++ b/previews/PR1410/examples/Physics.html @@ -85,8 +85,8 @@ 0 0 -1/2 0 0 0 0 -3/2]

    As you can see, the function is passed two objects: an OpName and a SiteType. The strings "Sz" and "S=3/2" are also part of the type of these objects, and have the meaning of which operator name we are defining and which site type these operators are defined for.

    The body of this overload of ITensors.op constructs and returns a Julia matrix which gives the matrix elements of the operator we are defining.

    Once this function is defined, and if you have an Index such as

    s = Index(4,"S=3/2")

    then, for example, you can get the "Sz" operator for this Index and print it out by doing:

    Sz = op("Sz",s)
     println(Sz)
    ITensor ord=2
    -Dim 1: (dim=4|id=333|"S=3/2")'
    -Dim 2: (dim=4|id=333|"S=3/2")
    +Dim 1: (dim=4|id=77|"S=3/2")'
    +Dim 2: (dim=4|id=77|"S=3/2")
     NDTensors.Dense{Float64, Vector{Float64}}
      4×4
      1.5  0.0   0.0   0.0
    @@ -125,4 +125,4 @@
        0   0  √3  0]
     
     

    Now let's look at each part of the code above.

    The space function

    In the previous code example above, we discussed that the function space tells the ITensor library the basic information about how to construct an Index associated with a special Index tag, in this case the tag "S=3/2". As in that code formula, if the user does not request that quantum numbers be included (the case conserve_qns=false) then all that the space function returns is the number 4, indicating that a "S=3/2" Index should be of dimension 4.

    But if the conserve_qns keyword argument gets set to true, the space function we defined above returns an array of QN=>Int pairs. (The notation a=>b in Julia constructs a Pair object.) Each pair in the array denotes a subspace. The QN part of each pair says what quantum number the subspace has, and the integer following it indicates the dimension of the subspace.

    After defining the space function this way, you can write code like:

    s = siteind("S=3/2"; conserve_qns=true)

    to obtain a single "S=3/2" Index which carries quantum number information. The siteind function built into ITensor relies on your custom space function to ask how to construct a "S=3/2" Index but also includes some other Index tags which are conventional for all site indices.

    You can now also call code like:

    N = 100
    -sites = siteinds("S=3/2",N; conserve_qns=true)

    to obtain an array of N "S=3/2" indices which carry quantum numbers.

    The op Function in the Quantum Number Case

    Note that the op function overloads are exactly the same as for the more basic case of defining an "S=3/2" Index type that does not carry quantum numbers. There is no need to upgrade any of the op functions for the QN-conserving case. The reason is that all QN, block-sparse information about an ITensor is deduced from the indices of the tensor, and setting elements of such tensors does not require any other special code.

    However, only operators which have a well-defined QN flux–-meaning they always change the quantum number of a state they act on by a well-defined amount–-can be used in practice in the case of QN conservation. Attempting to build an operator, or any ITensor, without a well-defined QN flux out of QN-conserving indices will result in a run time error. An example of an operator that would lead to such an error would be the "Sx" spin operator since it alternately increases $S^z$ or decreases $S^z$ depending on the state it acts on, thus it does not have a well-defined QN flux. But it is perfectly fine to define an op overload for the "Sx" operator and to make this operator when working with dense, non-QN-conserving ITensors or when $S^z$ is not conserved.

    +sites = siteinds("S=3/2",N; conserve_qns=true)

    to obtain an array of N "S=3/2" indices which carry quantum numbers.

    The op Function in the Quantum Number Case

    Note that the op function overloads are exactly the same as for the more basic case of defining an "S=3/2" Index type that does not carry quantum numbers. There is no need to upgrade any of the op functions for the QN-conserving case. The reason is that all QN, block-sparse information about an ITensor is deduced from the indices of the tensor, and setting elements of such tensors does not require any other special code.

    However, only operators which have a well-defined QN flux–-meaning they always change the quantum number of a state they act on by a well-defined amount–-can be used in practice in the case of QN conservation. Attempting to build an operator, or any ITensor, without a well-defined QN flux out of QN-conserving indices will result in a run time error. An example of an operator that would lead to such an error would be the "Sx" spin operator since it alternately increases $S^z$ or decreases $S^z$ depending on the state it acts on, thus it does not have a well-defined QN flux. But it is perfectly fine to define an op overload for the "Sx" operator and to make this operator when working with dense, non-QN-conserving ITensors or when $S^z$ is not conserved.

    diff --git a/previews/PR1410/faq/DMRG.html b/previews/PR1410/faq/DMRG.html index df7d95bb11..196ad7df6d 100644 --- a/previews/PR1410/faq/DMRG.html +++ b/previews/PR1410/faq/DMRG.html @@ -15,4 +15,4 @@ end hterms += "Sz",1,"Sz",N # term 'wrapping' around the ring -H = MPO(hterms,sites)

    For two-dimensional DMRG calculations, where the most common approach is to use periodic boundary conditions in the y-direction only, and not in the x-direction, you do a similar step in making your OpSum input to ITensor DMRG: you include terms wrapping around the periodic cylinder in the y direction but not in the x direction.

    However, fully periodic boundary conditions are only recommended for small systems when absolutely needed, and in general are not recommended. For a longer discussion of alternatives to using fully periodic boundaries, see the next section below.

    The reason fully periodic boundary conditions (periodic in x in 1D, and periodic in both x and y in 2D) are not recommended in general is that the DMRG algorithm, as we are defining it here, optimizes an open-boundary MPS. So if you input a periodic-boundary Hamiltonian, there is a kind of "mismatch" that happens where you can still get the correct answer, but it requires much more resources (a larger bond dimension and more sweeps) to get good accuracy. There has been some research into "truly" periodic DMRG, [Pippan] that is DMRG that optimizes an MPS with a ring-like topology, but it is not widely used, is still an open area of algorithm development, and is not currently available in ITensor.

    What boundary conditions should I choose: open, periodic, or infinite?

    One of the weaknesses of the density matrix renormalization group (DMRG), and its time-dependent or finite-temperature extensions, is that it works poorly with periodic boundary conditions. This stems from the fact that conventional DMRG optimizes over open-boundary matrix product state (MPS) wavefunctions whether or not the Hamiltonian includes periodic interactions.

    But this begs the question, when are periodic boundary conditions (PBC) really needed? DMRG offers some compelling alternatives to PBC:

    However, there are a handful of cases where PBC remains preferable despite the extra overhead. A few such cases are:

    (Note that in the remaining discussion, by PBC I mean fully periodic boundary conditions in all directions. For the case of DMRG applied to quasi-two-dimensional systems, it remains a good practice to use periodic boundaries in the shorter direction, while still using open (or infinite) boundaries in the longer direction along the DMRG/MPS path.)

    Below I discuss more about the problems with using PBC, as well as some misconceptions about when PBC seems necessary even though there are better alternatives.

    Drawbacks of Periodic Boundary Conditions

    Periodic boundary conditions are straightforward to implement in conventional DMRG. The simplest approach is to include a "long bond" directly connecting site 1 to site N in the Hamiltonian. However this naive approach has a major drawback: if open-boundary DMRG achieves a given accuracy when keeping $m$ states (bond dimension of size $m$), then to reach the same accuracy with PBC one must keep closer to $m^2$ states! The reason is that now every bond of the MPS not only carries local entanglement as with OBC, but also the entanglement between the first and last sites. (There is an alternative DMRG algorithm[Pippan] for periodic systems which may have better scaling than the above approach but has not been widely applied and tested, as far as I am aware, especially for 2D or critical systems .)

    The change in scaling from $m$ to $m^2$ is a severe problem. For example, many gapped one-dimensional systems only require about $m=100$ to reach good accuracy (truncation errors of less than 1E-9 or so). To reach the same accuracy with naive PBC would then require using 10,000 states, which can easily fill the RAM of a typical desktop computer for a large enough system, not to mention the extra time needed to work with larger matrices.

    But poor scaling is not the only drawback of PBC. Systems that exhibit spontaneous symmetry breaking are simple to work with under OBC, where one has the additional freedom of applying edge pinning terms to drive the bulk into a specific symmetry sector. Using edge pinning reduces the bulk entanglement and makes measuring order parameters straightforward. Similarly one can use infinite DMRG to directly observe symmetry breaking effects.

    But under PBC, order parameters remain equal to zero and can only be accessed through correlation functions. Though using correlation functions is often presented as the "standard" or "correct" approach, such reasoning pre-supposes that PBC is the best choice. Recent work in the quantum Monte Carlo community demonstrates that open boundaries with pinning fields can actually be a superior approach.[Assaad]

    Cases Where Periodic BC Seems Necessary, But Open/Infinite BC Can be Better

    Below are some cases where periodic boundary conditions seem to be necessary at a first glance. But in many of these cases, not only can open or infinite boundaries be just as successful, they can even be the better choice.

    In conclusion, consider carefully whether you really need to use periodic boundary conditions, as they impose a steep computational cost within DMRG. Periodic BC can actually be worse for the very types of measurements where they are often presented as the best or "standard" choice. Many of the issues periodic boundaries circumvent can be avoided more elegantly by using infinite DMRG, or when that is not applicable, by using open boundary conditions with sufficient care.

    +H = MPO(hterms,sites)

    For two-dimensional DMRG calculations, where the most common approach is to use periodic boundary conditions in the y-direction only, and not in the x-direction, you do a similar step in making your OpSum input to ITensor DMRG: you include terms wrapping around the periodic cylinder in the y direction but not in the x direction.

    However, fully periodic boundary conditions are only recommended for small systems when absolutely needed, and in general are not recommended. For a longer discussion of alternatives to using fully periodic boundaries, see the next section below.

    The reason fully periodic boundary conditions (periodic in x in 1D, and periodic in both x and y in 2D) are not recommended in general is that the DMRG algorithm, as we are defining it here, optimizes an open-boundary MPS. So if you input a periodic-boundary Hamiltonian, there is a kind of "mismatch" that happens where you can still get the correct answer, but it requires much more resources (a larger bond dimension and more sweeps) to get good accuracy. There has been some research into "truly" periodic DMRG, [Pippan] that is DMRG that optimizes an MPS with a ring-like topology, but it is not widely used, is still an open area of algorithm development, and is not currently available in ITensor.

    What boundary conditions should I choose: open, periodic, or infinite?

    One of the weaknesses of the density matrix renormalization group (DMRG), and its time-dependent or finite-temperature extensions, is that it works poorly with periodic boundary conditions. This stems from the fact that conventional DMRG optimizes over open-boundary matrix product state (MPS) wavefunctions whether or not the Hamiltonian includes periodic interactions.

    But this begs the question, when are periodic boundary conditions (PBC) really needed? DMRG offers some compelling alternatives to PBC:

    However, there are a handful of cases where PBC remains preferable despite the extra overhead. A few such cases are:

    (Note that in the remaining discussion, by PBC I mean fully periodic boundary conditions in all directions. For the case of DMRG applied to quasi-two-dimensional systems, it remains a good practice to use periodic boundaries in the shorter direction, while still using open (or infinite) boundaries in the longer direction along the DMRG/MPS path.)

    Below I discuss more about the problems with using PBC, as well as some misconceptions about when PBC seems necessary even though there are better alternatives.

    Drawbacks of Periodic Boundary Conditions

    Periodic boundary conditions are straightforward to implement in conventional DMRG. The simplest approach is to include a "long bond" directly connecting site 1 to site N in the Hamiltonian. However this naive approach has a major drawback: if open-boundary DMRG achieves a given accuracy when keeping $m$ states (bond dimension of size $m$), then to reach the same accuracy with PBC one must keep closer to $m^2$ states! The reason is that now every bond of the MPS not only carries local entanglement as with OBC, but also the entanglement between the first and last sites. (There is an alternative DMRG algorithm[Pippan] for periodic systems which may have better scaling than the above approach but has not been widely applied and tested, as far as I am aware, especially for 2D or critical systems .)

    The change in scaling from $m$ to $m^2$ is a severe problem. For example, many gapped one-dimensional systems only require about $m=100$ to reach good accuracy (truncation errors of less than 1E-9 or so). To reach the same accuracy with naive PBC would then require using 10,000 states, which can easily fill the RAM of a typical desktop computer for a large enough system, not to mention the extra time needed to work with larger matrices.

    But poor scaling is not the only drawback of PBC. Systems that exhibit spontaneous symmetry breaking are simple to work with under OBC, where one has the additional freedom of applying edge pinning terms to drive the bulk into a specific symmetry sector. Using edge pinning reduces the bulk entanglement and makes measuring order parameters straightforward. Similarly one can use infinite DMRG to directly observe symmetry breaking effects.

    But under PBC, order parameters remain equal to zero and can only be accessed through correlation functions. Though using correlation functions is often presented as the "standard" or "correct" approach, such reasoning pre-supposes that PBC is the best choice. Recent work in the quantum Monte Carlo community demonstrates that open boundaries with pinning fields can actually be a superior approach.[Assaad]

    Cases Where Periodic BC Seems Necessary, But Open/Infinite BC Can be Better

    Below are some cases where periodic boundary conditions seem to be necessary at a first glance. But in many of these cases, not only can open or infinite boundaries be just as successful, they can even be the better choice.

    In conclusion, consider carefully whether you really need to use periodic boundary conditions, as they impose a steep computational cost within DMRG. Periodic BC can actually be worse for the very types of measurements where they are often presented as the best or "standard" choice. Many of the issues periodic boundaries circumvent can be avoided more elegantly by using infinite DMRG, or when that is not applicable, by using open boundary conditions with sufficient care.

    diff --git a/previews/PR1410/faq/Development.html b/previews/PR1410/faq/Development.html index abbb2a1de1..8d907c4e87 100644 --- a/previews/PR1410/faq/Development.html +++ b/previews/PR1410/faq/Development.html @@ -1,2 +1,2 @@ -ITensor Development FAQs · ITensors.jl

    ITensor Development Frequently Asked Questions

    What are the steps to contribute code to ITensor?

    1. Please contact us (support at itensor.org) if you are planning to submit a major contribution (more than a few lines of code, say). If so, we would like to discuss your plan and design before you spend significant time on it, to increase the chances we will merge your pull request.

    2. Fork the ITensors.jl Github repo, create a new branch and make changes (commits) on that branch. ITensor imposes code formatting for contributions. Please run using JuliaFormatter; format(".") in the project directory to ensure formatting. As an alternative you may also use pre-commit. Install pre-commit with e.g. pip install pre-commit, then run pre-commit install in the project directory in order for pre-commit to run automatically before any commit.

    3. Run the ITensor unit tests by going into the test/ folder and running julia runtests.jl. To run individual test scripts, start a Julia REPL (interactive terminal) session and include each script, such as include("itensor.jl").

    4. Push your new branch and changes to your forked repo. Github will give you the option to make a pull request (PR) out of your branch that will be submitted to us, and which you can view under the list of ITensors.jl pull requests. If your PR's tests pass and we approve your changes, we will merge it or ask you to merge it. If you merge your PR, please use the Squash and Merge option. We may also ask you to make more changes to bring your PR in line with our design goals or technical requirements.

    +ITensor Development FAQs · ITensors.jl

    ITensor Development Frequently Asked Questions

    What are the steps to contribute code to ITensor?

    1. Please contact us (support at itensor.org) if you are planning to submit a major contribution (more than a few lines of code, say). If so, we would like to discuss your plan and design before you spend significant time on it, to increase the chances we will merge your pull request.

    2. Fork the ITensors.jl Github repo, create a new branch and make changes (commits) on that branch. ITensor imposes code formatting for contributions. Please run using JuliaFormatter; format(".") in the project directory to ensure formatting. As an alternative you may also use pre-commit. Install pre-commit with e.g. pip install pre-commit, then run pre-commit install in the project directory in order for pre-commit to run automatically before any commit.

    3. Run the ITensor unit tests by going into the test/ folder and running julia runtests.jl. To run individual test scripts, start a Julia REPL (interactive terminal) session and include each script, such as include("itensor.jl").

    4. Push your new branch and changes to your forked repo. Github will give you the option to make a pull request (PR) out of your branch that will be submitted to us, and which you can view under the list of ITensors.jl pull requests. If your PR's tests pass and we approve your changes, we will merge it or ask you to merge it. If you merge your PR, please use the Squash and Merge option. We may also ask you to make more changes to bring your PR in line with our design goals or technical requirements.

    diff --git a/previews/PR1410/faq/HPC.html b/previews/PR1410/faq/HPC.html index 02c48d0a81..a8e1e0f593 100644 --- a/previews/PR1410/faq/HPC.html +++ b/previews/PR1410/faq/HPC.html @@ -1,2 +1,2 @@ -High-Performance Computing FAQs · ITensors.jl

    High Performance Computing (HPC) Frequently Asked Questions

    My code is using a lot of RAM - what can I do about this?

    Tensor network algorithms can often use a large amount of RAM. On top of this essential fact, the Julia programming languge is "garbage collected" which means that unused memory isn't given back to the operating system right away, but only when the Julia runtime dynamically reclaims it. When your code allocates memory very rapidly, this can lead to high memory usage overall.

    Fortunately there are various steps you can take to keep the memory usage of your code under control.

    1. Avoid Repeatedly Allocating, Especially in Fast or "Hot" Loops

    More memory gets used whenever your code "allocates", which happens most commonly when you use dynamic storage types like Vector and Matrix. If you have a code pattern where you allocate or resize an array or vector inside a 'hot' loop, meaning a loop that iterates quickly very many times, the memory from the previous allocations may pile up very quickly before the next garbage collector run.

    To avoid this, allocate the array once before the loop begins if possible, then overwrite its contents during each iteration. More generally, try as much as possible to estimate the sizes of dynamic resources ahead of time. Or do one allocation that creates a large enough "workspace" that dynamic algorithms can reuse part of without reallocating the whole workspace (i.e. making a large array once then using portions of it when smaller arrays are needed).

    2. Use the --heap-size-hint Flag

    A simple step you can take to help with overall memory usage is to pass the --heap-size-hint flag to the Julia program when you start it. For example, you can call Julia as:

    julia --heap-size-hint=60G

    When you pass this heap size, Julia will try to keep the memory usage at or below this value if possible.

    In cases where this does not work, your code simply may be allocating too much memory. Be sure not to allocate over and over again inside of "hot" loops which execute many times.

    Another possibility is that you are simply working with a tensor network with large bond dimensions, which may fundamentally use a lot of memory. In those cases, you can try to use features such as "write to disk mode" of the ITensor DMRG code or other related techniques. (See the write_when_maxdim_exceeds keyword of the ITensor dmrg function.)

    3. In Rare Case, Force a Garbage Collection Run

    In some rare cases, such as when your code cannot be optimized to avoid any more allocations or when the --heap-size-hint provided above is not affecting the behavior of the Julia garbage collector, you can force the garbage collector (GC) to run at a specific point in your code by calling:

    GC.gc()

    Alternatively, you can call GC.gc(true) to force a "full run" rather than just collecting a more 'young' subset of previous allocations.

    While this approach works well to reduce memory usage, it can have the unfortunate downside of slowing down your code each time the garbage collector runs, which can be especially harmful to multithreaded or parallel algorithms. Therefore, if this approach must be used try calling GC.gc() as infrequently as possible and ideally only in the outermost functions and loops of your code (highest levels of your code).

    Can Julia Be Used to Perform Parallel, Distributed Calculations on Large Clusters?

    Yes. The Julia ecosystem offers multiple approaches to parallel computing across multiple machines including on large HPC clusters and including GPU resources.

    For an overall view of some of these options, the Julia on HPC Clusters website is a good resource.

    Some of the leading approaches to parallelism in Julia are:

    • MPI, through the MPI.jl package. Has the advantage of optionally using an MPI backend that is optimized for a particular cluster and possibly using fast interconnects like Infiniband.
    • Dagger, a framework for parallel computing across all kinds of resources, like CPUs and GPUs, and across multiple threads and multiple servers.
    • Distributed. Part of the base Julia library, giving tools to perform calculations distributed across multiple machines.

    Does My Cluster Admin Have to Install Julia for Me? What are the Best Practices for Installing Julia on Clusters?

    The most common approach to installing and using Julia on clusters is for users to install their own Julia binary and dependencies, which is quite easy to do. However, for certain libraries like MPI.jl, there may be MPI backends that are preferred by the cluster administrator. Fortunately, it is possible for admins to set global defaults for such backends and other library preferences.

    For more information on best practices for installing Julia on clusters, see the Julia on HPC Clusters website.

    +High-Performance Computing FAQs · ITensors.jl

    High Performance Computing (HPC) Frequently Asked Questions

    My code is using a lot of RAM - what can I do about this?

    Tensor network algorithms can often use a large amount of RAM. On top of this essential fact, the Julia programming languge is "garbage collected" which means that unused memory isn't given back to the operating system right away, but only when the Julia runtime dynamically reclaims it. When your code allocates memory very rapidly, this can lead to high memory usage overall.

    Fortunately there are various steps you can take to keep the memory usage of your code under control.

    1. Avoid Repeatedly Allocating, Especially in Fast or "Hot" Loops

    More memory gets used whenever your code "allocates", which happens most commonly when you use dynamic storage types like Vector and Matrix. If you have a code pattern where you allocate or resize an array or vector inside a 'hot' loop, meaning a loop that iterates quickly very many times, the memory from the previous allocations may pile up very quickly before the next garbage collector run.

    To avoid this, allocate the array once before the loop begins if possible, then overwrite its contents during each iteration. More generally, try as much as possible to estimate the sizes of dynamic resources ahead of time. Or do one allocation that creates a large enough "workspace" that dynamic algorithms can reuse part of without reallocating the whole workspace (i.e. making a large array once then using portions of it when smaller arrays are needed).

    2. Use the --heap-size-hint Flag

    A simple step you can take to help with overall memory usage is to pass the --heap-size-hint flag to the Julia program when you start it. For example, you can call Julia as:

    julia --heap-size-hint=60G

    When you pass this heap size, Julia will try to keep the memory usage at or below this value if possible.

    In cases where this does not work, your code simply may be allocating too much memory. Be sure not to allocate over and over again inside of "hot" loops which execute many times.

    Another possibility is that you are simply working with a tensor network with large bond dimensions, which may fundamentally use a lot of memory. In those cases, you can try to use features such as "write to disk mode" of the ITensor DMRG code or other related techniques. (See the write_when_maxdim_exceeds keyword of the ITensor dmrg function.)

    3. In Rare Case, Force a Garbage Collection Run

    In some rare cases, such as when your code cannot be optimized to avoid any more allocations or when the --heap-size-hint provided above is not affecting the behavior of the Julia garbage collector, you can force the garbage collector (GC) to run at a specific point in your code by calling:

    GC.gc()

    Alternatively, you can call GC.gc(true) to force a "full run" rather than just collecting a more 'young' subset of previous allocations.

    While this approach works well to reduce memory usage, it can have the unfortunate downside of slowing down your code each time the garbage collector runs, which can be especially harmful to multithreaded or parallel algorithms. Therefore, if this approach must be used try calling GC.gc() as infrequently as possible and ideally only in the outermost functions and loops of your code (highest levels of your code).

    Can Julia Be Used to Perform Parallel, Distributed Calculations on Large Clusters?

    Yes. The Julia ecosystem offers multiple approaches to parallel computing across multiple machines including on large HPC clusters and including GPU resources.

    For an overall view of some of these options, the Julia on HPC Clusters website is a good resource.

    Some of the leading approaches to parallelism in Julia are:

    • MPI, through the MPI.jl package. Has the advantage of optionally using an MPI backend that is optimized for a particular cluster and possibly using fast interconnects like Infiniband.
    • Dagger, a framework for parallel computing across all kinds of resources, like CPUs and GPUs, and across multiple threads and multiple servers.
    • Distributed. Part of the base Julia library, giving tools to perform calculations distributed across multiple machines.

    Does My Cluster Admin Have to Install Julia for Me? What are the Best Practices for Installing Julia on Clusters?

    The most common approach to installing and using Julia on clusters is for users to install their own Julia binary and dependencies, which is quite easy to do. However, for certain libraries like MPI.jl, there may be MPI backends that are preferred by the cluster administrator. Fortunately, it is possible for admins to set global defaults for such backends and other library preferences.

    For more information on best practices for installing Julia on clusters, see the Julia on HPC Clusters website.

    diff --git a/previews/PR1410/faq/JuliaAndCpp.html b/previews/PR1410/faq/JuliaAndCpp.html index 852f62eb90..1a347c8cf5 100644 --- a/previews/PR1410/faq/JuliaAndCpp.html +++ b/previews/PR1410/faq/JuliaAndCpp.html @@ -1,2 +1,2 @@ -Programming Language (Julia, C++, ...) FAQs · ITensors.jl

    Programming Language (Julia, C++) Frequently Asked Questions

    Should I use the Julia or C++ version of ITensor?

    We recommend the Julia version of ITensor for most people, because:

    • Julia ITensor has more and newer features than C++ ITensor, and we are developing it more rapidly
    • Julia is a more productive language than C++ with more built-in features, such as linear algebra, iteration tools, etc.
    • Julia is a compiled language with performance rivaling C++ (see next question below for a longer discussion)
    • Julia has a rich ecosystem with a package manager, many well-designed libraries, and helpful tutorials

    Even if Julia is not available by default on your computer cluster, it is easy to set up your own local install of Julia on a cluster.

    However, some good reasons to use the C++ version of ITensor are:

    • using ITensor within existing C++ codes
    • you already have expertise in C++ programming
    • multithreading support in C++, such as with OpenMP, offer certain sophisticated features compared to Julia multithreading (though Julia's support for multithreading has other benefits such as composability and is rapidly improving)
    • you need other specific features of C++, such as control over memory management or instant start-up times

    Which is faster: Julia or C++ ?

    Julia and C++ offer about the same performance.

    Each language gets compiled to optimized assembly code and offer arrays and containers which can efficiently stored and iterated. Well-written Julia code can be even faster than comparable C++ codes in many cases.

    The longer answer is of course that it depends:

    • Julia is a more productive language than C++, with many highly-optimized libraries for numerical computing tasks, and excellent tools for profiling and benchmarking. These features help significantly to tune Julia codes for optimal performance.
    • C++ offers much more fine-grained control over memory management, which can enhance performance in certain applications and control memory usage.
    • Julia codes can slow down significantly during refactoring or when introducing new code if certain best practices are not followed. The most important of these is writing type-stable code. For more details see the Performance Tips section of the Julia documentation.
    • C++ applications start instantly, while Julia codes can be slow to start. However, if this start-up time is subtracted, the rest of the time of running a Julia application is similar to C++.

    Why did you choose Julia over Python for ITensor?

    Julia offers much better performance than Python, while still having nearly all of Python's benefits. One consequence is that ITensor can be written purely in Julia, whereas to write high-performance Python libraries it is necessary to implement many parts in C or C++ (the "two-language problem").

    The main reasons Julia codes can easily outperform Python codes are:

    1. Julia is a (just-in-time) compiled language with functions specialized for the types of the arguments passed to them
    2. Julia arrays and containers are specialized to the types they contain, and perform similarly to C or C++ arrays when all elements have the same type
    3. Julia has sophisticated support for multithreading while Python has significant problems with multithreading

    Of course there are some drawbacks of Julia compared to Python, including a less mature ecosystem of libraries (though it is simple to call Python libraries from Julia using PyCall), and less widespread adoption.

    Is Julia ITensor a wrapper around the C++ version?

    No. The Julia version of ITensor is a complete, ground-up port of the ITensor library to the Julia language and is written 100% in Julia.

    +Programming Language (Julia, C++, ...) FAQs · ITensors.jl

    Programming Language (Julia, C++) Frequently Asked Questions

    Should I use the Julia or C++ version of ITensor?

    We recommend the Julia version of ITensor for most people, because:

    • Julia ITensor has more and newer features than C++ ITensor, and we are developing it more rapidly
    • Julia is a more productive language than C++ with more built-in features, such as linear algebra, iteration tools, etc.
    • Julia is a compiled language with performance rivaling C++ (see next question below for a longer discussion)
    • Julia has a rich ecosystem with a package manager, many well-designed libraries, and helpful tutorials

    Even if Julia is not available by default on your computer cluster, it is easy to set up your own local install of Julia on a cluster.

    However, some good reasons to use the C++ version of ITensor are:

    • using ITensor within existing C++ codes
    • you already have expertise in C++ programming
    • multithreading support in C++, such as with OpenMP, offer certain sophisticated features compared to Julia multithreading (though Julia's support for multithreading has other benefits such as composability and is rapidly improving)
    • you need other specific features of C++, such as control over memory management or instant start-up times

    Which is faster: Julia or C++ ?

    Julia and C++ offer about the same performance.

    Each language gets compiled to optimized assembly code and offer arrays and containers which can efficiently stored and iterated. Well-written Julia code can be even faster than comparable C++ codes in many cases.

    The longer answer is of course that it depends:

    • Julia is a more productive language than C++, with many highly-optimized libraries for numerical computing tasks, and excellent tools for profiling and benchmarking. These features help significantly to tune Julia codes for optimal performance.
    • C++ offers much more fine-grained control over memory management, which can enhance performance in certain applications and control memory usage.
    • Julia codes can slow down significantly during refactoring or when introducing new code if certain best practices are not followed. The most important of these is writing type-stable code. For more details see the Performance Tips section of the Julia documentation.
    • C++ applications start instantly, while Julia codes can be slow to start. However, if this start-up time is subtracted, the rest of the time of running a Julia application is similar to C++.

    Why did you choose Julia over Python for ITensor?

    Julia offers much better performance than Python, while still having nearly all of Python's benefits. One consequence is that ITensor can be written purely in Julia, whereas to write high-performance Python libraries it is necessary to implement many parts in C or C++ (the "two-language problem").

    The main reasons Julia codes can easily outperform Python codes are:

    1. Julia is a (just-in-time) compiled language with functions specialized for the types of the arguments passed to them
    2. Julia arrays and containers are specialized to the types they contain, and perform similarly to C or C++ arrays when all elements have the same type
    3. Julia has sophisticated support for multithreading while Python has significant problems with multithreading

    Of course there are some drawbacks of Julia compared to Python, including a less mature ecosystem of libraries (though it is simple to call Python libraries from Julia using PyCall), and less widespread adoption.

    Is Julia ITensor a wrapper around the C++ version?

    No. The Julia version of ITensor is a complete, ground-up port of the ITensor library to the Julia language and is written 100% in Julia.

    diff --git a/previews/PR1410/faq/JuliaPkg.html b/previews/PR1410/faq/JuliaPkg.html index 17ac7a2f11..e579fb7424 100644 --- a/previews/PR1410/faq/JuliaPkg.html +++ b/previews/PR1410/faq/JuliaPkg.html @@ -1,3 +1,3 @@ Julia Package Manager FAQs · ITensors.jl

    Julia Package Manager Frequently Asked Questions

    What if I can't upgrade ITensors.jl to the latest version?

    Sometimes you may find that doing ] update ITensors or equivalently doing ] up ITensors within Julia package manager mode doesn't result in the ITensors package actually being upgraded. You may see that the current version you have remains stuck to a version that is lower than the latest one which you can check here.

    What is most likely going on is that you have other packages installed which are blocking ITensors from being updated.

    To get more information into which packages may be doing this, and what versions they are requiring, you can do the following. First look up the latest version of ITensors.jl. Let's say for this example that it is v0.3.0.

    Next, input the following command while in package manager mode:

    julia> ]
    -pkg> add ITensors@v0.3.0

    If the package manager cannot update to this version, it will list all of the other packages that are blocking this from happening and give information about why. To go into a little more depth, each package has a compatibility or "compat" entry in its Project.toml file which says which versions of the ITensors package it is compatible with. If these versions do not include the latest one, perhaps because the package has not been updated, then it can block the ITensors package from being updated on your system.

    Generally the solution is to just update each of these packages, then try again to update ITensors. If that does not work, then check the following

    • Are any of the blocking packages in "dev mode" meaning you called dev PackageName on them in the past? Try doing free PackageName if so to bring them out of dev mode.
    • Are any of the blocking packages unregistered packages that were installed through a GitHub repo link? If so, you may need to do something like add https://github.com/Org/PackageName#main to force update that package to the latest code available on its main branch.

    If you still can't get the ITensors package update, feel free to post a question or contact us for help.

    +pkg> add ITensors@v0.3.0

    If the package manager cannot update to this version, it will list all of the other packages that are blocking this from happening and give information about why. To go into a little more depth, each package has a compatibility or "compat" entry in its Project.toml file which says which versions of the ITensors package it is compatible with. If these versions do not include the latest one, perhaps because the package has not been updated, then it can block the ITensors package from being updated on your system.

    Generally the solution is to just update each of these packages, then try again to update ITensors. If that does not work, then check the following

    If you still can't get the ITensors package update, feel free to post a question or contact us for help.

    diff --git a/previews/PR1410/faq/QN.html b/previews/PR1410/faq/QN.html index 528c57a5b4..bb1b46170a 100644 --- a/previews/PR1410/faq/QN.html +++ b/previews/PR1410/faq/QN.html @@ -1,4 +1,4 @@ Quantum Number (QN) FAQs · ITensors.jl

    Quantum Number Frequently Asked Questions

    Can I mix different types of quantum numbers within the same system?

    Yes, you can freely mix quantum numbers (QNs) of different types. For example, you can make the sites of your systems alternate between sites carrying spin "Sz" QNs and fermion sites carrying particle number "Nf" QNs. The QNs will not mix with each other and will separately be conserved to the original value you set for your initial wavefunction.

    How can I separately conserve QNs which have the same name?

    If you have two physically distinct types of sites, such as "Qudit" sites, but which carry identically named QNs called "Number", and you want the qudit number to be separately conserved within each type of site, you must make the QN names different for the two types of sites.

    For example, the following line of code will make an array of site indices with the qudit number QN having the name "Number_odd" on odd sites and "Number_even" on even sites:

    sites = [isodd(n) ? siteind("Qudit", n; dim=10, conserve_qns=true, qnname_number="Number_odd")
                       : siteind("Qudit", n; dim=2, conserve_qns=true, qnname_number="Number_even")
    -                  for n in 1:2*L]

    (You may have to collapse the above code into a single line for it to run properly.)

    + for n in 1:2*L]

    (You may have to collapse the above code into a single line for it to run properly.)

    diff --git a/previews/PR1410/faq/RelationshipToOtherLibraries.html b/previews/PR1410/faq/RelationshipToOtherLibraries.html index bb2552e222..f745292c2c 100644 --- a/previews/PR1410/faq/RelationshipToOtherLibraries.html +++ b/previews/PR1410/faq/RelationshipToOtherLibraries.html @@ -1,2 +1,2 @@ -Relationship of ITensor to other tensor libraries FAQs · ITensors.jl

    Relationship of ITensor to other tensor libraries

    Here we will describe the relationship of ITensor to more traditional Julia Arrays or deep learning libraries like TensorFlow and PyTorch. There are a few things that distinguish ITensor from those approaches:

    1. ITensors have dimensions with labels that get passed around, which makes it simple to perform certain operations like contraction, addition, and tensor decompositions with a high level interface, independent of memory layout. This is along the same lines as Julia packages like NamedDims.jl and AxisArrays.jl and libraries in Python like xarray, however I would argue that the ITensor approach is a little more sophisticated (the dimensions have more metadata which makes them easier to manipulate for different situations, random ids to help avoid name clashes, etc.). This design was inspired by the needs of tensor network algorithms, where there are many tensor dimensions in the computation (of which many of them are dynamically created during the calculation), but would be helpful for writing other algorithms too.

    2. The ITensor type has a dynamic high level interface, where the type itself is mutable and the data can be swapped out. This allows for conveniently allocating the data of an ITensor on the fly "as needed", which makes for a nicer, more flexible interface (like initializing an empty ITensor before a loop, and filling it with the correct data type when the first value is set), at the expense of a small overhead for accessing data in the ITensor. We have found this tradeoff is worth it, since we expect ITensors to be used for medium to large scale calculations where operations on the tensors like contraction, addition, and tensor decomposition dominate the cost of the calculation, and code can be designed with function barriers to speed up operations when data is being accessed repeatedly.

    3. Another feature that ITensor has that goes beyond what is available in standard Julia, TensorFlow, and PyTorch is tensors which are symmetric under a group action. The physical interpretation of these tensors are ones that have a conserved quantity (like a quantum state with a conserved number of particles), so that feature is more physics-oriented, but could have applications in other areas like machine learning as well. In practice, these tensors are block sparse, and have extra metadata on the dimensions labeling representations of the group.

    4. Based on the features above, the ITensor library provides high level implementations of tensor network algorithms (algebraic operations of very high dimensional tensors, such as addition, multiplication, and finding dominant eigenvectors). In general these algorithms can (and have been) written on top of other libraries like standard Julia Arrays/AD, PyTorch, or TensorFlow, but they might have various downsides (a less convenient interface for dealing with tensor operations, no support for the types of symmetric tensors we often need, limited support for tensors with complex numbers in the case of libraries like PyTorch, though perhaps that has improved since I last checked, etc.).

    Although ITensor has primarily focused on quantum physics and quantum computing applications, there is work using ITensor for machine learning applications (so far focused on applications of tensor networks to machine learning, so no neural network calculations yet as far as I know). In general, these different libraries (ITensor, Flux, PyTorch, TensorFlow) are biased towards their specific methods and application areas that they are used for the most: ITensor is more biased towards tensor network calculations and quantum physics/quantum computing applications, based on the available features and interface, while PyTorch and TensorFlow are more biased towards neural network calculations. However, our goal would be to provide more features to ITensor that would make it useful for neural network applications as well, such as better support for slicing operations.

    +Relationship of ITensor to other tensor libraries FAQs · ITensors.jl

    Relationship of ITensor to other tensor libraries

    Here we will describe the relationship of ITensor to more traditional Julia Arrays or deep learning libraries like TensorFlow and PyTorch. There are a few things that distinguish ITensor from those approaches:

    1. ITensors have dimensions with labels that get passed around, which makes it simple to perform certain operations like contraction, addition, and tensor decompositions with a high level interface, independent of memory layout. This is along the same lines as Julia packages like NamedDims.jl and AxisArrays.jl and libraries in Python like xarray, however I would argue that the ITensor approach is a little more sophisticated (the dimensions have more metadata which makes them easier to manipulate for different situations, random ids to help avoid name clashes, etc.). This design was inspired by the needs of tensor network algorithms, where there are many tensor dimensions in the computation (of which many of them are dynamically created during the calculation), but would be helpful for writing other algorithms too.

    2. The ITensor type has a dynamic high level interface, where the type itself is mutable and the data can be swapped out. This allows for conveniently allocating the data of an ITensor on the fly "as needed", which makes for a nicer, more flexible interface (like initializing an empty ITensor before a loop, and filling it with the correct data type when the first value is set), at the expense of a small overhead for accessing data in the ITensor. We have found this tradeoff is worth it, since we expect ITensors to be used for medium to large scale calculations where operations on the tensors like contraction, addition, and tensor decomposition dominate the cost of the calculation, and code can be designed with function barriers to speed up operations when data is being accessed repeatedly.

    3. Another feature that ITensor has that goes beyond what is available in standard Julia, TensorFlow, and PyTorch is tensors which are symmetric under a group action. The physical interpretation of these tensors are ones that have a conserved quantity (like a quantum state with a conserved number of particles), so that feature is more physics-oriented, but could have applications in other areas like machine learning as well. In practice, these tensors are block sparse, and have extra metadata on the dimensions labeling representations of the group.

    4. Based on the features above, the ITensor library provides high level implementations of tensor network algorithms (algebraic operations of very high dimensional tensors, such as addition, multiplication, and finding dominant eigenvectors). In general these algorithms can (and have been) written on top of other libraries like standard Julia Arrays/AD, PyTorch, or TensorFlow, but they might have various downsides (a less convenient interface for dealing with tensor operations, no support for the types of symmetric tensors we often need, limited support for tensors with complex numbers in the case of libraries like PyTorch, though perhaps that has improved since I last checked, etc.).

    Although ITensor has primarily focused on quantum physics and quantum computing applications, there is work using ITensor for machine learning applications (so far focused on applications of tensor networks to machine learning, so no neural network calculations yet as far as I know). In general, these different libraries (ITensor, Flux, PyTorch, TensorFlow) are biased towards their specific methods and application areas that they are used for the most: ITensor is more biased towards tensor network calculations and quantum physics/quantum computing applications, based on the available features and interface, while PyTorch and TensorFlow are more biased towards neural network calculations. However, our goal would be to provide more features to ITensor that would make it useful for neural network applications as well, such as better support for slicing operations.

    diff --git a/previews/PR1410/getting_started/DebugChecks.html b/previews/PR1410/getting_started/DebugChecks.html index a1e2d9a0d3..ad18b29264 100644 --- a/previews/PR1410/getting_started/DebugChecks.html +++ b/previews/PR1410/getting_started/DebugChecks.html @@ -35,4 +35,4 @@ [8] noprime(::ITensor) @ ITensors ~/.julia/packages/ITensors/cu9Bo/src/itensor.jl:1211 [9] top-level scope - @ REPL[7]:1

    You can track where debug checks are located in the code here, and add your own debug checks to your own code by wrapping your code with the macro ITensors.@debug_check.

    + @ REPL[7]:1

    You can track where debug checks are located in the code here, and add your own debug checks to your own code by wrapping your code with the macro ITensors.@debug_check.

    diff --git a/previews/PR1410/getting_started/Installing.html b/previews/PR1410/getting_started/Installing.html index 2ce1353468..a92b98525e 100644 --- a/previews/PR1410/getting_started/Installing.html +++ b/previews/PR1410/getting_started/Installing.html @@ -3,4 +3,4 @@ $ mkdir -p bin $ wget https://julialang-s3.julialang.org/bin/linux/x64/1.7/julia-1.7.2-linux-x86_64.tar.gz $ tar xvzf julia-1.7.2-linux-x86_64.tar.gz -$ ln -s julia-1.7.2/bin/julia bin/julia

    If you want to install Julia 1.6.6, you would change 1.7 to 1.6 and 1.7.2 to 1.6.6. In general we recommend using the current stable release of Julia, which you can find out by going to the Julia Downloads page. We also don't recommend using versions of Julia below 1.6, which are no longer compatible with ITensors.jl as of ITensors 0.3.

    After these steps, you should be able to type julia from your terminal to run Julia in interactive mode. If that works, then you have the Julia language and can run it in all the usual ways. If it does not work, you may need to log out and back in, and check that the bin directory is in your program execution path (PATH environment variable).

    Explanation of the sample commands above:

    Installing ITensor (ITensors.jl Package)

    Installing the Julia version of ITensor is easy once you have the Julia language installed. For more information about installing Julia, please see the Julia language downloads page.

    Once you have installed Julia on your machine,

    1. Enter the command julia to launch an interactive Julia session (a.k.a. the Julia "REPL")
    2. Type ] to enter the package manager (pkg> prompt should now show)
    3. Enter the command add ITensors
    4. After installation completes, press backspace to return to the normal julia> prompt
    5. Optional but Recommended: Enter the command julia> using ITensors; ITensors.compile() to compile a large fraction of the ITensor library code and following the instructions afterward to make an alias for loading a pre-built ITensor system image with Julia. This step can take up to 10 minutes to complete but only has to be done once for each version of ITensor. See the section on compiling ITensor for more information.

    Sample screenshot:

    +$ ln -s julia-1.7.2/bin/julia bin/julia

    If you want to install Julia 1.6.6, you would change 1.7 to 1.6 and 1.7.2 to 1.6.6. In general we recommend using the current stable release of Julia, which you can find out by going to the Julia Downloads page. We also don't recommend using versions of Julia below 1.6, which are no longer compatible with ITensors.jl as of ITensors 0.3.

    After these steps, you should be able to type julia from your terminal to run Julia in interactive mode. If that works, then you have the Julia language and can run it in all the usual ways. If it does not work, you may need to log out and back in, and check that the bin directory is in your program execution path (PATH environment variable).

    Explanation of the sample commands above:

    Installing ITensor (ITensors.jl Package)

    Installing the Julia version of ITensor is easy once you have the Julia language installed. For more information about installing Julia, please see the Julia language downloads page.

    Once you have installed Julia on your machine,

    1. Enter the command julia to launch an interactive Julia session (a.k.a. the Julia "REPL")
    2. Type ] to enter the package manager (pkg> prompt should now show)
    3. Enter the command add ITensors
    4. After installation completes, press backspace to return to the normal julia> prompt
    5. Optional but Recommended: Enter the command julia> using ITensors; ITensors.compile() to compile a large fraction of the ITensor library code and following the instructions afterward to make an alias for loading a pre-built ITensor system image with Julia. This step can take up to 10 minutes to complete but only has to be done once for each version of ITensor. See the section on compiling ITensor for more information.

    Sample screenshot:

    diff --git a/previews/PR1410/getting_started/NextSteps.html b/previews/PR1410/getting_started/NextSteps.html index 28fdf09688..6f61ffae69 100644 --- a/previews/PR1410/getting_started/NextSteps.html +++ b/previews/PR1410/getting_started/NextSteps.html @@ -1,2 +1,2 @@ -Next Steps · ITensors.jl
    +Next Steps · ITensors.jl
    diff --git a/previews/PR1410/getting_started/RunningCodes.html b/previews/PR1410/getting_started/RunningCodes.html index f547311e84..edc5f4b599 100644 --- a/previews/PR1410/getting_started/RunningCodes.html +++ b/previews/PR1410/getting_started/RunningCodes.html @@ -20,4 +20,4 @@ end main(; d1 = 4, d2 = 5)

    which can be useful in interactive mode, particularly if you might want to run your code with a variety of different arguments.

    Running a Script

    Now say you put the above code into a file named code.jl. Then you can run this code on the command line as follows

    $ julia code.jl

    This script-like mode of running Julia is convenient for running longer jobs, such as on a cluster.

    Running Interactively

    However, sometimes you want to do rapid development when first writing and testing a code. For this kind of work, the long startup and compilation times currently incurred by the Julia compiler can be a nuisance. Fortunately a nice solution is to alternate between modifying your code then running it by loading it into an already running Julia session.

    To set up this kind of session, take the following steps:

    1. Enter the interactive mode of Julia, by inputting the command julia on the command line. You will now be in the Julia "REPL" (read-eval-print loop) with the prompt julia> on the left of your screen.

    2. To run a code such as the code.jl file discussed above, input the command

      julia> include("code.jl")

      Note that you must be in the same folder as code.jl for this to work; otherwise input the entire path to the code.jl file. The code will run and you will see its output in the REPL.

    3. Now say you want to modify and re-run the code. To do this, just edit the file in an editor in another window, without closing your Julia session. Now run the command

      julia> include("code.jl")

      again and your updated code will run, but this time skipping any of the precompilation overhead incurred on previous steps.

    The above steps to running a code interactively has a big advantage that you only have to pay the startup time of compiling ITensor and other libraries you are using once. Further changes to your code only incur very small extra compilation times, facilitating rapid development.

    Compiling an ITensor System Image

    The above strategy of running code in the Julia REPL (interactive mode) works well, but still incurs a large start-up penalty for the first run of your code. Fortunately there is a nice way around this issue too: compiling ITensors.jl and making a system image built by the PackageCompiler.jl library.

    To use this approach, we have provided a convenient one-line command:

    julia> using ITensors; ITensors.compile()

    Once ITensors.jl is installed, you can just run this command in an interactive Julia session. It can take a few minutes to run, but you only have to run it once for a given version of ITensors.jl. When it is done, it will create a file sys_itensors.so in the directory ~/.julia/sysimages/.

    To use the compiled system image together with Julia, run the julia command (for interactive mode or scripts) in the following way:

    $ julia --sysimage ~/.julia/sysimages/sys_itensors.so

    A convenient thing to do is to make an alias in your shell for this command. To do this, edit your .bashrc or .zshrc or similar file for the shell you use by adding the following line:

    alias julia_itensors="julia --sysimage ~/.julia/sysimages/sys_itensors.so -e \"using ITensors\" -i "

    where of course you can use the command name you like when defining the alias. Now running commands like julia_itensors code.jl or julia_itensors to start an interactive session will have the ITensor system image pre-loaded and you will notice significantly faster startup times. The arguments -e \"using ITensors\" -i make it so that running julia_itensors also loads the ITensor library as soon as Julia starts up, so that you don't have to type using ITensors every time.

    Using a Compiled Sysimage in Jupyter or VS Code

    If you have compiled a sysimage for ITensor as shown above, you can use it in Jupyter by running the following code:

    using IJulia
    -installkernel("julia_ITensors","--sysimage=~/.julia/sysimages/sys_itensors.so")

    in the Julia REPL (Julia console).

    To load the ITensor sysimage in VS Code, you can add

    "--sysimage ~/.julia/sysimages/sys_itensors.so"

    as an argument under the julia.additionalArgs setting in your Settings.json file.

    For more information on the above, see the following Julia Discourse post.

    +installkernel("julia_ITensors","--sysimage=~/.julia/sysimages/sys_itensors.so")

    in the Julia REPL (Julia console).

    To load the ITensor sysimage in VS Code, you can add

    "--sysimage ~/.julia/sysimages/sys_itensors.so"

    as an argument under the julia.additionalArgs setting in your Settings.json file.

    For more information on the above, see the following Julia Discourse post.

    diff --git a/previews/PR1410/index.html b/previews/PR1410/index.html index c8e07fbbef..df06007f98 100644 --- a/previews/PR1410/index.html +++ b/previews/PR1410/index.html @@ -161,4 +161,4 @@ After sweep 3 energy=-138.940080155429 maxlinkdim=92 maxerr=1.00E-10 time=4.522 After sweep 4 energy=-138.940086009318 maxlinkdim=100 maxerr=1.05E-10 time=11.644 After sweep 5 energy=-138.940086058840 maxlinkdim=96 maxerr=1.00E-10 time=12.771 -Final energy = -138.94008605883985

    You can find more examples of running dmrg and related algorithms here.

    +Final energy = -138.94008605883985

    You can find more examples of running dmrg and related algorithms here.

    diff --git a/previews/PR1410/search.html b/previews/PR1410/search.html index e5ed7d6f25..cc09efa6e3 100644 --- a/previews/PR1410/search.html +++ b/previews/PR1410/search.html @@ -1,2 +1,2 @@ -Search · ITensors.jl

    Loading search...

      +Search · ITensors.jl

      Loading search...

        diff --git a/previews/PR1410/search_index.js b/previews/PR1410/search_index.js index a6b91985d4..0586031221 100644 --- a/previews/PR1410/search_index.js +++ b/previews/PR1410/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"DMRGObserver.html#DMRGObserver","page":"DMRGObserver","title":"DMRGObserver","text":"","category":"section"},{"location":"DMRGObserver.html","page":"DMRGObserver","title":"DMRGObserver","text":"A DMRGObserver is a type of observer which offers certain useful, general purpose capabilities for DMRG calculations such as measuring custom local observables at each step and stopping DMRG early if certain energy convergence conditions are met.","category":"page"},{"location":"DMRGObserver.html#Sample-Usage","page":"DMRGObserver","title":"Sample Usage","text":"","category":"section"},{"location":"DMRGObserver.html","page":"DMRGObserver","title":"DMRGObserver","text":"In the following example, we have already made a Hamiltonian MPO H and initial MPS psi0 for a system of spins whose sites have an associated \"Sz\" operator defined. We construct a DMRGObserver which measures \"Sz\" on each site at each step of DMRG, and also stops the calculation early if the energy no longer changes to a relative precision of 1E-7.","category":"page"},{"location":"DMRGObserver.html","page":"DMRGObserver","title":"DMRGObserver","text":"Sz_observer = DMRGObserver([\"Sz\"],sites,energy_tol=1E-7)\n\nenergy, psi = dmrg(H,psi0,sweeps,observer=Sz_observer)\n\nfor (sw,Szs) in enumerate(measurements(Sz_observer)[\"Sz\"])\n println(\"Total Sz after sweep $sw = \", sum(Szs)/N)\nend","category":"page"},{"location":"DMRGObserver.html#Constructors","page":"DMRGObserver","title":"Constructors","text":"","category":"section"},{"location":"DMRGObserver.html","page":"DMRGObserver","title":"DMRGObserver","text":"DMRGObserver(;energy_tol::Float64,minsweeps::Int)\nDMRGObserver(ops::Vector{String},sites::Vector{<:Index};energy_tol::Float64,minsweeps::Int)","category":"page"},{"location":"DMRGObserver.html#ITensors.ITensorMPS.DMRGObserver-Tuple{}","page":"DMRGObserver","title":"ITensors.ITensorMPS.DMRGObserver","text":"DMRGObserver(;energy_tol=0.0,\n minsweeps=2,\n energy_type=Float64)\n\nConstruct a DMRGObserver by providing the energy tolerance used for early stopping, and minimum number of sweeps that must be done.\n\nOptional keyword arguments:\n\nenergy_tol: if the energy from one sweep to the next no longer changes by more than this amount, stop after the current sweep\nminsweeps: do at least this many sweeps\nenergy_type: type to use when storing energies at each step\n\n\n\n\n\n","category":"method"},{"location":"DMRGObserver.html#ITensors.ITensorMPS.DMRGObserver-Tuple{Vector{String}, Vector{<:Index}}","page":"DMRGObserver","title":"ITensors.ITensorMPS.DMRGObserver","text":"DMRGObserver(ops::Vector{String},\n sites::Vector{<:Index};\n energy_tol=0.0,\n minsweeps=2,\n energy_type=Float64)\n\nConstruct a DMRGObserver, provide an array of ops of operator names which are strings recognized by the op function. Each of these operators will be measured on every site during every step of DMRG and the results recorded inside the DMRGOberver for later analysis. The array sites is the basis of sites used to define the MPS and MPO for the DMRG calculation.\n\nOptionally, one can provide an energy tolerance used for early stopping, and minimum number of sweeps that must be done.\n\nOptional keyword arguments:\n\nenergy_tol: if the energy from one sweep to the next no longer changes by more than this amount, stop after the current sweep\nminsweeps: do at least this many sweeps\nenergy_type: type to use when storing energies at each step\n\n\n\n\n\n","category":"method"},{"location":"DMRGObserver.html#Methods","page":"DMRGObserver","title":"Methods","text":"","category":"section"},{"location":"DMRGObserver.html","page":"DMRGObserver","title":"DMRGObserver","text":"measurements(::DMRGObserver)\nDMRGMeasurement\nenergies(::DMRGObserver)","category":"page"},{"location":"DMRGObserver.html#ITensors.ITensorMPS.measurements-Tuple{DMRGObserver}","page":"DMRGObserver","title":"ITensors.ITensorMPS.measurements","text":"measurements(o::DMRGObserver)\n\nAfter using a DMRGObserver object o within a DMRG calculation, retrieve a dictionary of measurement results, with the keys being operator names and values being DMRGMeasurement objects.\n\n\n\n\n\n","category":"method"},{"location":"DMRGObserver.html#ITensors.ITensorMPS.DMRGMeasurement","page":"DMRGObserver","title":"ITensors.ITensorMPS.DMRGMeasurement","text":"A DMRGMeasurement object is an alias for Vector{Vector{Float64}}, in other words an array of arrays of real numbers.\n\nGiven a DMRGMeasurement M,the result for the measurement on sweep n and site i as M[n][i].\n\n\n\n\n\n","category":"type"},{"location":"DMRGObserver.html#ITensors.ITensorMPS.energies-Tuple{DMRGObserver}","page":"DMRGObserver","title":"ITensors.ITensorMPS.energies","text":"energies(o::DMRGObserver)\n\nAfter using a DMRGObserver object o within a DMRG calculation, retrieve an array of the energy after each sweep.\n\n\n\n\n\n","category":"method"},{"location":"HDF5FileFormats.html#HDF5-File-Formats","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"This page lists the formats for the HDF5 representations of various types in the ITensors module.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 is a portable file format which has a directory structure similar to a file system. In addition to containing \"groups\" (= directories) and \"datasets\" (= files), groups can have \"attributes\" appended to them, which are similar to 'tags' or 'keywords'. Unless otherwise specified, integers are 64 bit and are signed (H5T_STD_I64LE) unless explicitly stated. (For example, the \"id\" field of the Index type is stored as an unsigned 64 bit integer (H5T_STD_U64LE).)","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Each type in ITensor which is writeable to HDF5 is written to its own group, with the name of the group either specified by the user or specified to some default value when it is a subgroup of another ITensor type (for example, the Index type saves its TagSet in a subgroup named \"tags\").","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Each group corresponding to an ITensors type always carries the following attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"type\" –- a string such as Index or TagSet specifying the information necessary to determine the type of the object saved to the HDF5 group\n\"version\" –- an integer specifying the file format version used to store the data. This version is in general different from the release version of ITensors.jl. The purpose of the version number is to aid in maintaining backwards compatibility, while allowing the format to be occasionally changed.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"The C++ version of ITensor uses exactly the same file formats listed below, for the purpose of interoperability with the Julia version of ITensor, even though conventions such as the \"type\" field values are Julia-centric.","category":"page"},{"location":"HDF5FileFormats.html#tagset_hdf5","page":"HDF5 File Formats","title":"TagSet","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for the ITensors.TagSet type.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"TagSet\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"tags\" [string] = a comma separated string of the tags in the TagSet","category":"page"},{"location":"HDF5FileFormats.html#qn_hdf5","page":"HDF5 File Formats","title":"QN","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for the ITensors.QN type.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"QN\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"names\" [group] = array of strings (length 4) of names of quantum numbers\n\"vals\" [group] = array of integers (length 4) of quantum number values\n\"mods\" [group] = array of integers (length 4) of moduli of quantum numbers","category":"page"},{"location":"HDF5FileFormats.html#qnblocks_hdf5","page":"HDF5 File Formats","title":"QNBlocks","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for the ITensors.QNBlocks type. (Note: QNBlocks is equivalent to Vector{Pair{QN, Int64}}.)","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"QNBlocks\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"length\" [integer] = the number of blocks (length of Vector)\n\"dims\" [group] = array of (integer) dimensions of each block\n\"QN[n]\" [group] = these groups \"QN[1]\", \"QN[2]\", etc. correspond to the QN of each block","category":"page"},{"location":"HDF5FileFormats.html#index_hdf5","page":"HDF5 File Formats","title":"Index","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for the ITensors.Index type.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"Index\"\n\"space_type\" = \"Int\" if the Index is a regular, dense Index or \"QNBlocks\" if the Index is a QNIndex (carries QN subspace information)","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"id\" [unsigned integer] = id number of the Index\n\"dim\" [integer] = dimension of the Index\n\"dir\" [integer] = arrow direction of the Index, +1 for ITensors.Out and -1 for ITensors.In\n\"plev\" [integer] = prime level of the Index\n\"tags\" [group] = the TagSet of the Index","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Optional Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"space\" [group] = if the \"space_type\" attribute is \"QNBlocks\", this group is present and represents a QNBlocks object","category":"page"},{"location":"HDF5FileFormats.html#indexset_hdf5","page":"HDF5 File Formats","title":"IndexSet","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for types in the Union type ITensors.Indices which includes IndexSet and tuples of Index objects.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"IndexSet\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"length\" [integer] = number of indices\n\"index_n\" [group] = for n=1 to n=length each of these groups contains an Index","category":"page"},{"location":"HDF5FileFormats.html#itensor_hdf5","page":"HDF5 File Formats","title":"ITensor","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for the ITensors.ITensor type.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"ITensor\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"inds\" [group] = indices of the ITensor\n\"storage\" [group] = storage of the ITensor (note that some earlier versions of ITensors.jl may call this group \"store\")","category":"page"},{"location":"HDF5FileFormats.html#dense_hdf5","page":"HDF5 File Formats","title":"NDTensors.Dense","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for objects which are subtypes of ITensors.NDTensors.Dense.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"Dense{Float64}\" or \"Dense{ComplexF64}\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"data\" = array of either real or complex values (in the same dataset format used by the HDF5.jl library for storing Vector{Float64} or Vector{ComplexF64})","category":"page"},{"location":"HDF5FileFormats.html#blocksparse_hdf5","page":"HDF5 File Formats","title":"NDTensors.BlockSparse","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for objects which are subtypes of ITensors.NDTensors.BlockSparse.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"BlockSparse{Float64}\" or \"BlockSparse{ComplexF64}\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"ndims\" [integer] = number of dimensions (order) of the tensor\n\"offsets\" = block offset data flattened into an array of integers\n\"data\" = array of either real or complex values (in the same dataset format used by the HDF5.jl library for storing Vector{Float64} or Vector{ComplexF64})","category":"page"},{"location":"HDF5FileFormats.html#mps_hdf5","page":"HDF5 File Formats","title":"MPS","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for ITensors.MPS","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"MPS\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"length\" [integer] = number of tensors of the MPS\n\"rlim\" [integer] = right orthogonality limit\n\"llim\" [integer] = left orthogonality limit\n\"MPS[n]\" [group,ITensor] = each of these groups, where n=1,...,length, stores the nth ITensor of the MPS","category":"page"},{"location":"HDF5FileFormats.html#mpo_hdf5","page":"HDF5 File Formats","title":"MPO","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for ITensors.MPO","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"MPO\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"length\" [integer] = number of tensors of the MPO\n\"rlim\" [integer] = right orthogonality limit\n\"llim\" [integer] = left orthogonality limit\n\"MPO[n]\" [group,ITensor] = each of these groups, where n=1,...,length, stores the nth ITensor of the MPO","category":"page"},{"location":"tutorials/MPSTimeEvolution.html#MPS-Time-Evolution","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"","category":"section"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"An important application of matrix product state (MPS) tensor networks in physics is computing the time evolution of a quantum state under the dynamics of a Hamiltonian H. An accurate, efficient, and simple way to time evolve a matrix product state (MPS) is by using a Trotter decomposition of the time evolution operator U(t) = e^-i H t.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"The technique we will use is \"time evolving block decimation\" (TEBD). More simply it is just the idea of decomposing the time-evolution operator into a circuit of quantum 'gates' (two-site unitaries) using the Trotter-Suzuki approximation and applying these gates in a controlled way to an MPS.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Let's see how to set up and run a TEBD calculation using ITensor.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"The Hamiltonian H we will use is the one-dimensional Heisenberg model which is given by:","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"beginaligned\nH = sum_j=1^N-1 mathbfS_j cdot mathbfS_j+1 \n = sum_j=1^N-1 S^z_j S^z_j+1 + frac12 S^+_j S^-_j+1 + frac12 S^-_j S^+_j+1\nendaligned","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"The TEBD Method","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"When the Hamiltonian, like the one above, is a sum of local terms,","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"H = sum_j h_jj+1","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"where h_jj+1 acts on sites j and j+1, then a Trotter decomposition that is particularly well suited for use with MPS techniques is","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"e^-i tau H approx e^-i h_12 tau2 e^-i h_23 tau2 cdots e^-i h_N-1N tau2\ne^-i h_N-1N tau2 e^-i h_N-2N-1 tau2 cdots e^-i h_12 tau2 + O(tau^3)","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Note the factors of two in each exponential. Each factored exponential is known as a Trotter \"gate\".","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"We can visualize the resulting circuit that will be applied to the MPS as follows:","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"(Image: )","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"The error in the above decomposition is of order tau^3, so that will be the error accumulated per time step. Because of the time-step error, one takes tau to be small and then applies the above set of operators to an MPS as a single sweep, then does a number (ttau) of sweeps to evolve for a total time t. The total error will therefore scale as tau^2 with this scheme, though other sources of error may dominate for long times, or very small tau, such as truncation errors.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Let's take a look at the code to apply these Trotter gates to an MPS to time evolve it. Then we will break down the steps of the code in more detail.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"ITensor TEBD Time Evolution Code","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Let's look at an entire, working ITensor code that will do this calculation then discuss the main steps. (If you need help running the code below, see the getting started page on running ITensor codes.)","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"using ITensors\n\nlet\n N = 100\n cutoff = 1E-8\n tau = 0.1\n ttotal = 5.0\n\n # Make an array of 'site' indices\n s = siteinds(\"S=1/2\", N; conserve_qns=true)\n\n # Make gates (1,2),(2,3),(3,4),...\n gates = ITensor[]\n for j in 1:(N - 1)\n s1 = s[j]\n s2 = s[j + 1]\n hj =\n op(\"Sz\", s1) * op(\"Sz\", s2) +\n 1 / 2 * op(\"S+\", s1) * op(\"S-\", s2) +\n 1 / 2 * op(\"S-\", s1) * op(\"S+\", s2)\n Gj = exp(-im * tau / 2 * hj)\n push!(gates, Gj)\n end\n # Include gates in reverse order too\n # (N,N-1),(N-1,N-2),...\n append!(gates, reverse(gates))\n\n # Initialize psi to be a product state (alternating up and down)\n psi = MPS(s, n -> isodd(n) ? \"Up\" : \"Dn\")\n\n c = div(N, 2) # center site\n\n # Compute and print at each time step\n # then apply the gates to go to the next time\n for t in 0.0:tau:ttotal\n Sz = expect(psi, \"Sz\"; sites=c)\n println(\"$t $Sz\")\n\n t≈ttotal && break\n\n psi = apply(gates, psi; cutoff)\n normalize!(psi)\n end\n\n return\nend","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Steps of The Code","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"First we setsome parameters, like the system size N and time step tau to use.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"The line s = siteinds(\"S=1/2\",N;conserve_qns=true) defines an array of spin 1/2 tensor indices (Index objects) which will be the site or physical indices of the MPS.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Next we make an empty array gates = ITensor[] that will hold ITensors that will be our Trotter gates. Inside the for n=1:N-1 loop that follows the lines","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"hj = op(\"Sz\",s1) * op(\"Sz\",s2) +\n 1/2 * op(\"S+\",s1) * op(\"S-\",s2) +\n 1/2 * op(\"S-\",s1) * op(\"S+\",s2)","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"call the op function which reads the \"S=1/2\" tag on our site indices (sites j and j+1) and which then knows that we want the spin 1/ 2 version of the \"Sz\", \"S+\", and \"S-\" operators. The op function returns these operators as ITensors and we tensor product and add them together to compute the operator h_jj+1 defined as","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"h_jj+1 = S^z_j S^z_j+1 + frac12 S^+_j S^-_j+1 + frac12 S^-_j S^+_j+1","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"which we call hj in the code.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"To make the corresponding Trotter gate Gj we exponentiate hj times a factor -i tau2 and then append or push this onto the end of the gate array gates.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Gj = exp(-im * tau/2 * hj)\npush!(gates,Gj)","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Having made the gates for bonds (1,2),(2,3),(3,4), etc. we still need to append the gates in reverse order to complete the correct Trotter formula. Here we can conveniently do that by just calling the Julia append! function and supply a reversed version of the array of gates we have made so far. This can be done in a single line of code append!(gates,reverse(gates)).","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"The line of code psi = MPS(s, n -> isodd(n) ? \"Up\" : \"Dn\") initializes our MPS psi as a product state of alternating up and down spins.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"To carry out the time evolution we loop over the range of times from 0.0 to ttotal in steps of tau, using the Julia range notation 0.0:tau:ttotal to easily set up this loop as for t in 0.0:tau:ttotal.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Inside the loop, we use the expect function to measure the expected value of the \"Sz\" operator on the center site.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"To evolve the MPS to the next time, we call the function","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"psi = apply(gates, psi; cutoff)","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"which applies the array of ITensors called gates to our current MPS psi, truncating the MPS at each step using the truncation error threshold supplied as the variable cutoff.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"The apply function is smart enough to determine which site indices each gate has, and then figure out where to apply it to our MPS. It automatically handles truncating the MPS and can even handle non-nearest-neighbor gates, though that feature is not used in this example.","category":"page"},{"location":"faq/JuliaAndCpp.html#Programming-Language-(Julia,-C)-Frequently-Asked-Questions","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++) Frequently Asked Questions","text":"","category":"section"},{"location":"faq/JuliaAndCpp.html#Should-I-use-the-Julia-or-C-version-of-ITensor?","page":"Programming Language (Julia, C++, ...) FAQs","title":"Should I use the Julia or C++ version of ITensor?","text":"","category":"section"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"We recommend the Julia version of ITensor for most people, because:","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Julia ITensor has more and newer features than C++ ITensor, and we are developing it more rapidly\nJulia is a more productive language than C++ with more built-in features, such as linear algebra, iteration tools, etc.\nJulia is a compiled language with performance rivaling C++ (see next question below for a longer discussion)\nJulia has a rich ecosystem with a package manager, many well-designed libraries, and helpful tutorials","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Even if Julia is not available by default on your computer cluster, it is easy to set up your own local install of Julia on a cluster.","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"However, some good reasons to use the C++ version of ITensor are:","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"using ITensor within existing C++ codes\nyou already have expertise in C++ programming\nmultithreading support in C++, such as with OpenMP, offer certain sophisticated features compared to Julia multithreading (though Julia's support for multithreading has other benefits such as composability and is rapidly improving)\nyou need other specific features of C++, such as control over memory management or instant start-up times","category":"page"},{"location":"faq/JuliaAndCpp.html#Which-is-faster:-Julia-or-C-?","page":"Programming Language (Julia, C++, ...) FAQs","title":"Which is faster: Julia or C++ ?","text":"","category":"section"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Julia and C++ offer about the same performance.","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Each language gets compiled to optimized assembly code and offer arrays and containers which can efficiently stored and iterated. Well-written Julia code can be even faster than comparable C++ codes in many cases.","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"The longer answer is of course that it depends:","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Julia is a more productive language than C++, with many highly-optimized libraries for numerical computing tasks, and excellent tools for profiling and benchmarking. These features help significantly to tune Julia codes for optimal performance.\nC++ offers much more fine-grained control over memory management, which can enhance performance in certain applications and control memory usage.\nJulia codes can slow down significantly during refactoring or when introducing new code if certain best practices are not followed. The most important of these is writing type-stable code. For more details see the Performance Tips section of the Julia documentation.\nC++ applications start instantly, while Julia codes can be slow to start. However, if this start-up time is subtracted, the rest of the time of running a Julia application is similar to C++.","category":"page"},{"location":"faq/JuliaAndCpp.html#Why-did-you-choose-Julia-over-Python-for-ITensor?","page":"Programming Language (Julia, C++, ...) FAQs","title":"Why did you choose Julia over Python for ITensor?","text":"","category":"section"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Julia offers much better performance than Python, while still having nearly all of Python's benefits. One consequence is that ITensor can be written purely in Julia, whereas to write high-performance Python libraries it is necessary to implement many parts in C or C++ (the \"two-language problem\").","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"The main reasons Julia codes can easily outperform Python codes are:","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Julia is a (just-in-time) compiled language with functions specialized for the types of the arguments passed to them\nJulia arrays and containers are specialized to the types they contain, and perform similarly to C or C++ arrays when all elements have the same type\nJulia has sophisticated support for multithreading while Python has significant problems with multithreading","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Of course there are some drawbacks of Julia compared to Python, including a less mature ecosystem of libraries (though it is simple to call Python libraries from Julia using PyCall), and less widespread adoption.","category":"page"},{"location":"faq/JuliaAndCpp.html#Is-Julia-ITensor-a-wrapper-around-the-C-version?","page":"Programming Language (Julia, C++, ...) FAQs","title":"Is Julia ITensor a wrapper around the C++ version?","text":"","category":"section"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"No. The Julia version of ITensor is a complete, ground-up port of the ITensor library to the Julia language and is written 100% in Julia.","category":"page"},{"location":"examples/MPSandMPO.html#MPS-and-MPO-Examples","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"The following examples demonstrate operations available in ITensor to work with matrix product state (MPS) (or tensor train) and matrix product operator (MPO) tensor networks.","category":"page"},{"location":"examples/MPSandMPO.html#Creating-an-MPS-from-a-Tensor","page":"MPS and MPO Examples","title":"Creating an MPS from a Tensor","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"A matrix product state (MPS) made of N tensors, each with one site or physical index, is a way of representing a single tensor with N indices. One way of obtaining the MPS form of an N-index tensor T is by repeatedly factorizing T into N separate tensors using a factorization such as the Singular Value Decomposition (SVD). This algorithm for obtaining an MPS is known in the mathematics literature as the \"tensor train SVD\" or \"TT-SVD\" algorithm.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"To turn an N-index (order-N) tensor T into an MPS, you can just construct an MPS by passing T as the first argument, along with keyword arguments that control the approximations used in factorizing T. Let's look at a few specific cases.","category":"page"},{"location":"examples/MPSandMPO.html#ITensor-to-MPS-Example","page":"MPS and MPO Examples","title":"ITensor to MPS Example","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"If you have a tensor T which is an ITensor and has indices i,j,k,l,m, you can create an MPS approximation of T where the MPS has site indices i,j,k,l,m as follows:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"cutoff = 1E-8\nmaxdim = 10\nT = randomITensor(i,j,k,l,m)\nM = MPS(T,(i,j,k,l,m);cutoff=cutoff,maxdim=maxdim)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Here we used a random ITensor for illustrative purposes, but it could be any ITensor and typically tensors with additional structure are more well approximated by MPS.","category":"page"},{"location":"examples/MPSandMPO.html#Julia-Tensor-to-MPS-Example","page":"MPS and MPO Examples","title":"Julia Tensor to MPS Example","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Another situation could be where you have a Julia array or Julia tensor of dimension d^N and want to approximate it as an MPS with N site indices, each of dimension d. For example, we could have the following random Julia array of dimension 2times 2times 2 times 2 times 2:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"d = 2\nN = 5\nA = randn(d,d,d,d,d)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Alternatively, the array could be just a one dimensional array of length d^N:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"A = randn(d^N)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"To convert this array to an MPS, we will first need a collection of Index objects to use as the site indices of the MPS. We can conveniently construct an array of four indices of dimension 2 as follows:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"sites = siteinds(d,N)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Finally, we can pass our array A and our sites to the MPS constructor along with parameters controlling the truncation level of the factorizations used:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"cutoff = 1E-8\nmaxdim = 10\nM = MPS(A,sites;cutoff=cutoff,maxdim=maxdim)","category":"page"},{"location":"examples/MPSandMPO.html#Obtaining-Elements-of-a-Tensor-Represented-by-an-MPS","page":"MPS and MPO Examples","title":"Obtaining Elements of a Tensor Represented by an MPS","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"A matrix product state (MPS) or tensor train (TT) is a format for representing a large tensor having N indices in terms of N smaller tensors. Given an MPS represeting a tensor T we can obtain a particular element T^s_1 s_2 s_3 cdots s_N of that tensor using code similar to the following code below.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"In the example code below we will obtain the element T^1211212221 of the tensor T which is (implicitly) defined by the MPS psi:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"using ITensors # hide\nlet # hide\nN = 10\ns = siteinds(2,N)\nchi = 4\npsi = randomMPS(s;linkdims=chi)\n\n# Make an array of integers of the element we\n# want to obtain\nel = [1,2,1,1,2,1,2,2,2,1]\n\nV = ITensor(1.)\nfor j=1:N\n V *= (psi[j]*state(s[j],el[j]))\nend\nv = scalar(V)\n\n# v is the element we wanted to obtain:\n@show v\nend # hide","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"The call to state(s[j],el[j]) in the code above makes a single-index ITensor with the Index s[j] and the entry at location el[j] set to 1.0, with all other entries set to 0.0. Contracting this tensor with the MPS tensor at site j can be viewed as \"clamping\" or \"fixing\" the index to a set value. The resulting tensors are contracted sequentially, overwriting the ITensor V, and the final scalar value of V is the tensor element we seek.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"See below for a visual depiction of what the above code is doing:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html#Expected-Value-of-Local-Operators","page":"MPS and MPO Examples","title":"Expected Value of Local Operators","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"When using an MPS to represent a quantum wavefunction psirangle a common operation is computing the expected value langlepsihatA_jpsirangle of a local operator hatA_j acting on site j. This can be accomplished efficiently and conveniently using the expect function as:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Avals = expect(psi,\"A\")","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"where \"A\" must be an operator associated with the physical site type, or site tags, of the sites of the MPS psi. For example, the operator name could be \"Sz\" for spin sites or \"Ntot\" for electron sites. (For more information about defining such operators yourself, see the section on Extending op Function Definitions.)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"As a concrete example, consider computing the expectation value of S^z_j on every site of an MPS representing a system of N spins of size S=12. In the following example we will use a random MPS of bond dimension chi=4 but the MPS could be obtained other ways such as through a DMRG calculation.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"using ITensors # hide\nN = 10\nchi = 4\nsites = siteinds(\"S=1/2\",N)\npsi = randomMPS(sites,chi)\nmagz = expect(psi,\"Sz\")\nfor (j,mz) in enumerate(magz)\n println(\"$j $mz\")\nend","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html#Expected-Values-of-MPO-Operators","page":"MPS and MPO Examples","title":"Expected Values of MPO Operators","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"When using an MPS to represent a quantum wavefunction psirangle another common operation is computing the expected value langlepsiWpsirangle of an operator W which is represented as a matrix product operator (MPO) tensor network. A key example could be the Hamiltonian defining a quantum system.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Given an MPO W and an MPS psi, you can compute langlepsiWpsirangle by using the function inner as follows:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"ex_W = inner(psi',W,psi)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"which will return a scalar that may be either real or complex, depending on the properties of psi and W.","category":"page"},{"location":"examples/MPSandMPO.html#Computing-Correlation-Functions","page":"MPS and MPO Examples","title":"Computing Correlation Functions","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"In addition to expected values of local operators discussed above, another type of observable that is very important in physics studies are correlation functions of the form","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"C_ij = langlepsi A_i B_j psirangle","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"These can be computed efficiently for an MPS psi in ITensor using the correlation_matrix function:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"C = correlation_matrix(psi,\"A\",\"B\")","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"where \"A\" and \"B\" must be an operator names associated with the physical site type, or site tags, of the sites of the MPS psi. For example, these strings could be \"Sz\", \"S+\", or \"S-\" for spin sites, or \"Cdagup\" and \"Cup\" for electron sites. (For more information about defining such operators yourself, see the section on Extending op Function Definitions.)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"As a concrete example, say we have an MPS psi for a system of spins and want to compute the correlator langlepsiS^z_i S^z_jpsirangle. We can compute this as:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"zzcorr = correlation_matrix(psi,\"Sz\",\"Sz\")","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"See the correlation_matrix docs for more details about additional arguments you can pass to this function.","category":"page"},{"location":"examples/MPSandMPO.html#Applying-a-Single-site-Operator-to-an-MPS","page":"MPS and MPO Examples","title":"Applying a Single-site Operator to an MPS","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"In many applications one needs to modify a matrix product state (MPS) by multiplying it with an operator that acts only on a single site. This is actually a very straightforward operation and this formula shows you how to do it in ITensor.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Say we have an operator G^s_3_s_3 which which acts non-trivially on site 3 of our MPS psi as in the following diagram:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"To carry out this operation, contract the operator G with the MPS tensor for site 3, removing the prime from the s_3 index afterward:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"newA = G * psi[3]\nnewA = noprime(newA)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Finally, put the new tensor back into MPS psi to update its third MPS tensor:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"psi[3] = newA","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Afterward, we can visualize the modified MPS as:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"As a technical note, if you are working in a context where gauge or orthogonality properties of the MPS are important, such as in time evolution using two-site gates, then you may want to call psi = orthogonalize(psi, 3) before modifying the tensor at site 3, which will ensure that the MPS remains in a well-defined orthogonal gauge centered on site 3. Modifying a tensor which is left- or right-orthogonal (i.e. not the \"center\" tensor of the gauge) will destroy the gauge condition and require extra operations to restore it. (Calling orthogonalize method will automatically fix this but will have to do extra work to do so.)","category":"page"},{"location":"examples/MPSandMPO.html#Applying-a-Two-site-Operator-to-an-MPS","page":"MPS and MPO Examples","title":"Applying a Two-site Operator to an MPS","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"A very common operation with matrix product states (MPS) is multiplication by a two-site operator or \"gate\" which modifies the MPS. This procedure can be carried out in an efficient, controlled way which is adaptive in the MPS bond dimension.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Say we have an operator G^s_3 s_4_s_3 s_4 which is our gate and which acts on physical sites 3 and 4 of our MPS psi, as in the following diagram:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"To apply this gate in a controlled manner, first 'gauge' the MPS psi such that either site 3 or 4 is the orthogonality center. Here we make site 3 the center:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"psi = orthogonalize(psi, 3)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"The other MPS tensors are now either left-orthogonal or right-orthogonal and can be left out of further steps without producing incorrect results.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Next, contract the gate tensor G with the MPS tensors for sites 3 and 4","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"wf = (psi[3] * psi[4]) * G\nwf = noprime(wf)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Finally, use the singular value decomposition (SVD) to factorize the resulting tensor, multiplying the singular values into either U or V. Assign these two tensors back into the MPS to update it.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"inds3 = uniqueinds(psi[3],psi[4])\nU,S,V = svd(wf,inds3,cutoff=1E-8)\npsi[3] = U\npsi[4] = S*V","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"The call to uniqueinds(psi[3]) analyzes the indices of psi[3] and psi[4] and finds any which are unique to just psi[3], saving this collection of indices as inds3. Passing this collection of indices to the svd function tells it to treat any indices that are unique to psi[3] as the indices which should go onto the U tensor afterward. We also set a truncation error cutoff of 1E-8 in the call to svd to truncate the smallest singular values and control the size of the resulting MPS. Other cutoff values can be used, depending on the desired accuracy, as well as limits on the maximum bond dimension (maxdim keyword argument).","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Complete code example","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"psi = orthogonalize(psi, 3)\n\nwf = (psi[3] * psi[4]) * G\nwf = noprime(wf)\n\ninds3 = uniqueinds(psi[3], psi[4])\nU, S, V = svd(wf, inds3; cutoff=1E-8)\npsi[3] = U\npsi[4] = S * V","category":"page"},{"location":"examples/MPSandMPO.html#Computing-the-Entanglement-Entropy-of-an-MPS","page":"MPS and MPO Examples","title":"Computing the Entanglement Entropy of an MPS","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"A key advantage of using the matrix product state (MPS) format to represent quantum wavefunctions is that it allows one to efficiently compute the entanglement entropy of any left-right bipartition of the system in one dimension, or for a two-dimensional system any \"cut\" along the MPS path.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Say that we have obtained an MPS psi of length N and we wish to compute the entanglement entropy of a bipartition of the system into a region \"A\" which consists of sites 1,2,...,b and a region B consisting of sites b+1,b+2,...,N.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Then the following code formula can be used to accomplish this task:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"psi = orthogonalize(psi, b)\nU,S,V = svd(psi[b], (linkinds(psi, b-1)..., siteinds(psi, b)...))\nSvN = 0.0\nfor n=1:dim(S, 1)\n p = S[n,n]^2\n SvN -= p * log(p)\nend","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"As a brief explanation of the code above, the call to psi = orthogonalize(psi, b) shifts the orthogonality center to site b of the MPS.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"The call to the svd routine says to treat the link (virtual or bond) Index connecting the b'th MPS tensor psi[b] and the b'th physical Index as \"row\" indices for the purposes of the SVD (these indices will end up on U, along with the Index connecting U to S).","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"The code in the for loop iterates over the diagonal elements of the S tensor (which are the singular values from the SVD), computes their squares to obtain the probabilities of observing the various states in the Schmidt basis (i.e. eigenvectors of the left-right bipartition reduced density matrices), and puts them into the von Neumann entanglement entropy formula S_textvN = - sum_n p_n logp_n.","category":"page"},{"location":"examples/MPSandMPO.html#Sampling-from-an-MPS","page":"MPS and MPO Examples","title":"Sampling from an MPS","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"A matrix product state (MPS) can be viewed as defining a probability distribution through the Born rule, as is the case when the MPS represents a quantum wavefunction. To sample from the distribution defined by an MPS, you can use the function sample provided in ITensor. For an MPS psi call to sample(psi) returns a random sample from the distribution defined by psi. (Note that each sample is drawn anew and not from a Markov chain seeded by a previous sample; this is possible because the algorithm for sampling MPS is a `perfect' sampling algorithm with no autocorrelation.)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"In more detail, say we have a set of N site indices s and define a random MPS with these sites:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"using ITensors # hide\nN = 10 # number of sites\nd = 3 # dimension of each site\nchi = 16 # bond dimension of the MPS\ns = siteinds(d,N)\npsi = randomMPS(s;linkdims=chi)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"We can now draw some samples from this MPS as","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"v1 = sample(psi)\nv2 = sample(psi)\nv3 = sample(psi)\nprintln(v1)\nprintln(v2)\nprintln(v3)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"The integers in each of the samples represent settings of each of the MPS indices in the \"computational basis\".","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"For reasons of efficiency, the sample function requires the MPS to be in orthogonal form, orthogonalized to the first site. If it is not already in this form, it can be brought into orthogonal form by calling psi = orthogonalize(psi, 1).","category":"page"},{"location":"examples/MPSandMPO.html#Write-and-Read-an-MPS-or-MPO-to-Disk-with-HDF5","page":"MPS and MPO Examples","title":"Write and Read an MPS or MPO to Disk with HDF5","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"info: Info\nMake sure to install the HDF5 package to use this feature. (Run julia> ] add HDF5 in the Julia REPL console.)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Writing an MPS to an HDF5 File","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Let's say you have an MPS psi which you have made or obtained from a calculation. To write it to an HDF5 file named \"myfile.h5\" you can use the following pattern:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"using HDF5\nf = h5open(\"myfile.h5\",\"w\")\nwrite(f,\"psi\",psi)\nclose(f)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Above, the string \"psi\" can actually be any string you want such as \"MPS psi\" or \"Result MPS\" and doesn't have to have the same name as the reference psi. Closing the file f is optional and you can also write other objects to the same file before closing it.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Reading an MPS from an HDF5 File","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Say you have an HDF5 file \"myfile.h5\" which contains an MPS stored as a dataset with the name \"psi\". (Which would be the situation if you wrote it as in the example above.) To read this ITensor back from the HDF5 file, use the following pattern:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"using HDF5\nf = h5open(\"myfile.h5\",\"r\")\npsi = read(f,\"psi\",MPS)\nclose(f)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Many functions which involve MPS, such as the dmrg function or the OpSum system require that you use an array of site indices which match the MPS. So when reading in an MPS from disk, do not construct a new array of site indices. Instead, you can obtain them like this: sites = siteinds(psi).","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"So for example, to create an MPO from an OpSum which has the same site indices as your MPS psi, do the following:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"os = OpSum()\n# Then put operators into os...\n\nsites = siteinds(psi) # Get site indices from your MPS\nH = MPO(os,sites)\n\n# Compute \nenergy_psi = inner(psi',H,psi)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Note the MPS argument to the read function, which tells Julia which read function to call and how to interpret the data stored in the HDF5 dataset named \"psi\". In the future we might lift the requirement of providing the type and have it be detected automatically from the data stored in the file.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Writing and Reading MPOs","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"To write or read MPOs to or from HDF5 files, just follow the examples above but use the type MPO when reading an MPO from the file instead of the type MPS.","category":"page"},{"location":"getting_started/RunningCodes.html#Running-ITensor-and-Julia-Codes","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"","category":"section"},{"location":"getting_started/RunningCodes.html#Basic-Example-Code-Template","page":"Running ITensor and Julia Codes","title":"Basic Example Code Template","text":"","category":"section"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"The basic outline of a code which uses the ITensor library is as follows","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"using ITensors\n\nlet\n # ... your own code goes here ...\n # For example:\n i = Index(2,\"i\")\n j = Index(3,\"j\")\n T = randomITensor(i,j)\n @show T\nend","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"The reason we recommend the let...end block is that code written in the Julia global scope can have some surprising behaviors. Putting your code into a let block avoids these issues.","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"Alternatively, you can wrap your code in a function:","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"using ITensors\n\nfunction main(; d1 = 2, d2 = 3)\n # ... your own code goes here ...\n # For example:\n i = Index(d1,\"i\")\n j = Index(d2,\"j\")\n T = randomITensor(i,j)\n @show T\nend\n\nmain(; d1 = 4, d2 = 5)","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"which can be useful in interactive mode, particularly if you might want to run your code with a variety of different arguments.","category":"page"},{"location":"getting_started/RunningCodes.html#Running-a-Script","page":"Running ITensor and Julia Codes","title":"Running a Script","text":"","category":"section"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"Now say you put the above code into a file named code.jl. Then you can run this code on the command line as follows","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"$ julia code.jl","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"This script-like mode of running Julia is convenient for running longer jobs, such as on a cluster.","category":"page"},{"location":"getting_started/RunningCodes.html#Running-Interactively","page":"Running ITensor and Julia Codes","title":"Running Interactively","text":"","category":"section"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"However, sometimes you want to do rapid development when first writing and testing a code. For this kind of work, the long startup and compilation times currently incurred by the Julia compiler can be a nuisance. Fortunately a nice solution is to alternate between modifying your code then running it by loading it into an already running Julia session.","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"To set up this kind of session, take the following steps:","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"Enter the interactive mode of Julia, by inputting the command julia on the command line. You will now be in the Julia \"REPL\" (read-eval-print loop) with the prompt julia> on the left of your screen.\nTo run a code such as the code.jl file discussed above, input the command\njulia> include(\"code.jl\")\nNote that you must be in the same folder as code.jl for this to work; otherwise input the entire path to the code.jl file. The code will run and you will see its output in the REPL.\nNow say you want to modify and re-run the code. To do this, just edit the file in an editor in another window, without closing your Julia session. Now run the command\njulia> include(\"code.jl\")\nagain and your updated code will run, but this time skipping any of the precompilation overhead incurred on previous steps.","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"The above steps to running a code interactively has a big advantage that you only have to pay the startup time of compiling ITensor and other libraries you are using once. Further changes to your code only incur very small extra compilation times, facilitating rapid development.","category":"page"},{"location":"getting_started/RunningCodes.html#Compiling-an-ITensor-System-Image","page":"Running ITensor and Julia Codes","title":"Compiling an ITensor System Image","text":"","category":"section"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"The above strategy of running code in the Julia REPL (interactive mode) works well, but still incurs a large start-up penalty for the first run of your code. Fortunately there is a nice way around this issue too: compiling ITensors.jl and making a system image built by the PackageCompiler.jl library.","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"To use this approach, we have provided a convenient one-line command:","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"julia> using ITensors; ITensors.compile()","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"Once ITensors.jl is installed, you can just run this command in an interactive Julia session. It can take a few minutes to run, but you only have to run it once for a given version of ITensors.jl. When it is done, it will create a file sys_itensors.so in the directory ~/.julia/sysimages/.","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"To use the compiled system image together with Julia, run the julia command (for interactive mode or scripts) in the following way:","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"$ julia --sysimage ~/.julia/sysimages/sys_itensors.so","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"A convenient thing to do is to make an alias in your shell for this command. To do this, edit your .bashrc or .zshrc or similar file for the shell you use by adding the following line:","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"alias julia_itensors=\"julia --sysimage ~/.julia/sysimages/sys_itensors.so -e \\\"using ITensors\\\" -i \"","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"where of course you can use the command name you like when defining the alias. Now running commands like julia_itensors code.jl or julia_itensors to start an interactive session will have the ITensor system image pre-loaded and you will notice significantly faster startup times. The arguments -e \\\"using ITensors\\\" -i make it so that running julia_itensors also loads the ITensor library as soon as Julia starts up, so that you don't have to type using ITensors every time.","category":"page"},{"location":"getting_started/RunningCodes.html#Using-a-Compiled-Sysimage-in-Jupyter-or-VS-Code","page":"Running ITensor and Julia Codes","title":"Using a Compiled Sysimage in Jupyter or VS Code","text":"","category":"section"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"If you have compiled a sysimage for ITensor as shown above, you can use it in Jupyter by running the following code:","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"using IJulia\ninstallkernel(\"julia_ITensors\",\"--sysimage=~/.julia/sysimages/sys_itensors.so\")","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"in the Julia REPL (Julia console).","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"To load the ITensor sysimage in VS Code, you can add","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"\"--sysimage ~/.julia/sysimages/sys_itensors.so\"","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"as an argument under the julia.additionalArgs setting in your Settings.json file.","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"For more information on the above, see the following Julia Discourse post.","category":"page"},{"location":"getting_started/NextSteps.html#Next-Steps","page":"Next Steps","title":"Next Steps","text":"","category":"section"},{"location":"getting_started/NextSteps.html","page":"Next Steps","title":"Next Steps","text":"Try one of the Tutorials in the next section of the ITensor documentation\nBrowse the Code Examples.\nRead the ITensor Paper for a long-form introduction to the design and main features of the ITensor library\nRead the Advanced ITensor Usage Guide\nMore Julia language tutorials and resources\nFrom zero to Julia!\nThink Julia\nOfficial Julia Language Manual\nList of Resources at julialang.org","category":"page"},{"location":"tutorials/DMRG.html#dmrg_tutorial","page":"DMRG","title":"DMRG Tutorial","text":"","category":"section"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"The density matrix renormalization group (DMRG) is an algorithm for computing eigenstates of Hamiltonians (or extremal eigenvectors of large, Hermitian matrices). It computes these eigenstates in the matrix product state (MPS) format.","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"Let's see how to set up and run a DMRG calculation using the ITensor library. We will be interested in finding the ground state of the quantum Hamiltonian H given by:","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"H = sum_j=1^N-1 mathbfS_j cdot mathbfS_j+1 = sum_j=1^N-1 S^z_j S^z_j+1 + frac12 S^+_j S^-_j+1 + frac12 S^-_j S^+_j+1","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"This Hamiltonian is known as the one-dimensional Heisenberg model and we will take the spins to be S=1 spins (spin-one spins). We will consider the case of N=100 and plan to do five sweeps of DMRG (five passes over the system).","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"ITensor DMRG Code","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"Let's look at an entire, working ITensor code that will do this calculation then discuss the main steps. If you need help running the code below, see the getting started page on Running ITensor and Julia Codes.","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"using ITensors\nlet\n N = 100\n sites = siteinds(\"S=1\",N)\n\n os = OpSum()\n for j=1:N-1\n os += \"Sz\",j,\"Sz\",j+1\n os += 1/2,\"S+\",j,\"S-\",j+1\n os += 1/2,\"S-\",j,\"S+\",j+1\n end\n H = MPO(os,sites)\n\n psi0 = randomMPS(sites,10)\n\n nsweeps = 5\n maxdim = [10,20,100,100,200]\n cutoff = [1E-10]\n\n energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff)\n\n return\nend","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"Steps of The Code","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"The first two lines","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"using ITensors # hide\nN = 100\nsites = siteinds(\"S=1\",N)","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"tells the function siteinds to make an array of ITensor Index objects which have the properties of S=1 spins. This means their dimension will be 3 and they will carry the \"S=1\" tag, which will enable the next part of the code to know how to make appropriate operators for them.","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"Try printing out some of these indices to verify their properties:","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"@show sites[1]","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"The next part of the code builds the Hamiltonian:","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"os = OpSum()\nfor j=1:N-1\n os += \"Sz\",j,\"Sz\",j+1\n os += 1/2,\"S+\",j,\"S-\",j+1\n os += 1/2,\"S-\",j,\"S+\",j+1\nend\nH = MPO(os,sites)","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"An OpSum is an object which accumulates Hamiltonian terms such as \"Sz\",1,\"Sz\",2 so that they can be summed afterward into a matrix product operator (MPO) tensor network. The line of code H = MPO(os,sites) constructs the Hamiltonian in the MPO format, with physical indices given by the array sites.","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"The line","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"psi0 = randomMPS(sites,10)","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"constructs an MPS psi0 which has the physical indices sites and a bond dimension of 10. It is made by a random quantum circuit that is reshaped into an MPS, so that it will have as generic and unbiased properties as an MPS of that size can have. This choice can help prevent the DMRG calculation from getting stuck in a local minimum.","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"The lines","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"nsweeps = 5\nmaxdim = [10,20,100,100,200]\ncutoff = [1E-10]","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"define the number of DMRG sweeps (five) we will instruct the code to do, as well as the parameters that will control the speed and accuracy of the DMRG algorithm within each sweep. The array maxdim limits the maximum MPS bond dimension allowed during each sweep and cutoff defines the truncation error goal of each sweep (if fewer values are specified than sweeps, the last value is used for all remaining sweeps).","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"Finally the call","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff)","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"runs the DMRG algorithm included in ITensor, using psi0 as an initial guess for the ground state wavefunction. The optimized MPS psi and its eigenvalue energy are returned.","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"After the dmrg function returns, you can take the returned MPS psi and do further calculations with it, such as measuring local operators or computing entanglement entropy.","category":"page"},{"location":"Multithreading.html#Multithreading","page":"Multithreading","title":"Multithreading","text":"","category":"section"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"Most modern computers, including laptops, have multiple cores (processing units) which can be used to perform multiple tasks at the same time and therefore speed up computations. Multithreading is a form of shared memory parallelism that makes use of these multiple cores that you may have available.","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"There are three primary sources of parallelization available to ITensors.jl. These are:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"BLAS/LAPACK multithreading (through whatever flavor you are using, i.e. OpenBLAS or MKL).\nThe Strided.jl package, which implements efficient multithreaded dense array permutations.\nBlock sparse multithreading (currently only for block sparse contractions) implemented in the NDTensors.jl package.","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"First, you can obtain the number of threads that are available to you with:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"julia> Sys.CPU_THREADS\n6","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"If your computations are dominated by large dense tensors, you likely want to make use of BLAS multithreading in order to multithread dense matrix multiplications and other linear algebra methods like SVD and QR decompositions. This will be on by default. The BLAS/LAPACK multithreading can be controlled in the usual way with environment variables such as by starting Julia with:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"$ MKL_NUM_THREADS=4 julia # Set the number of MKL threads to 4\n\n$ OPENBLAS_NUM_THREADS=4 julia # Set the number of OpenBLAS threads to 4\n\n$ OMP_NUM_THREADS=4 julia # Set the number of OpenMP threads to 4, which will be used by MKL or OpenBLAS if they are not specifically set","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"or at runtime from within Julia:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"julia> using LinearAlgebra\n\njulia> BLAS.vendor() # Check which BLAS you are using\n:mkl\n\njulia> using ITensors\n\njulia> BLAS.get_num_threads()\n6\n\njulia> BLAS.set_num_threads(4)\n\njulia> BLAS.get_num_threads()\n4","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"Note that in Julia v1.6, you will be able to use the command using LinearAlgebra; BLAS.get_num_threads().","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"We would highly recommend using MKL (see the installation instructions for how to do that), especially if you are using an Intel chip. How well BLAS multithreading will work depends on how much your calculations are dominated by large dense matrix operations (which is not always the case, especially if you are using QN conservation).","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"Currently, ITensors.jl makes use of the package Strided.jl for performant dense array permutations. It also provides multithreaded array permutations. If you start Julia with multiple threads, Strided multithreading is on by default:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"$ julia -t 4\n\njulia> Threads.nthreads()\n4\n\njulia> ITensors.Strided.get_num_threads()\n4","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"We find that this threading competes with BLAS threading as well as ITensors.jl's own block sparse multithreading, so if you are using Julia with multiple threads you may want to disable Strided.jl's threading with:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"julia> ITensors.Strided.disable_threads()\n1\n\njulia> ITensors.Strided.get_num_threads()\n1","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"in favor of either BLAS threading or ITensors.jl's block sparse threading.","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"Additionally, ITensors.jl, through the NDTensors.jl library, provides multithreaded block sparse operations. By default, this kind of threading is disabled. If your computations involve QN conserving tensors, you may want to consider enabling block sparse multithreading as described below.","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"ITensors.enable_threaded_blocksparse","category":"page"},{"location":"Multithreading.html#ITensors.enable_threaded_blocksparse","page":"Multithreading","title":"ITensors.enable_threaded_blocksparse","text":"ITensors.enable_threaded_blocksparse()\nITensors.disable_threaded_blocksparse()\n\nEnable or disable block sparse multithreading.\n\nReturns the current state of ITensors.using_threaded_blocksparse(), i.e. true if threaded block sparse was previously enabled, and false if threaded block sparse was previously disabled. This is helpful for turning block sparse threading on or off temporarily. For example:\n\nusing_threaded_blocksparse = ITensors.enable_threaded_blocksparse()\n# Run code that you want to be threaded\nif !using_threaded_blocksparse\n ITensors.disable_threaded_blocksparse()\nend\n\nNote that you need to start Julia with multiple threads. For example, to start Julia with 4 threads, you can use any of the following:\n\n$ julia --threads=4\n\n$ julia -t 4\n\n$ JULIA_NUM_THREADS=4 julia\n\nIn addition, we have found that it is best to disable BLAS and Strided multithreading when using block sparse multithreading. You can do that with the commands using LinearAlgebra; BLAS.set_num_threads(1) and ITensors.Strided.disable_threads().\n\nSee also: ITensors.enable_threaded_blocksparse, ITensors.disable_threaded_blocksparse, ITensors.using_threaded_blocksparse.\n\n\n\n\n\nenable_threaded_blocksparse(enable::Bool)\n\nenable_threaded_blocksparse(true) enables threaded block sparse operations (equivalent to enable_threaded_blocksparse()).\n\nenable_threaded_blocksparse(false) disables threaded block sparse operations (equivalent to enable_threaded_blocksparse()).\n\n\n\n\n\n","category":"function"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"Here is a simple example of using block sparse multithreading to speed up a sparse tensor contraction:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"using BenchmarkTools\nusing ITensors\nusing LinearAlgebra\n\nfunction main(; d = 20, order = 4)\n BLAS.set_num_threads(1)\n ITensors.Strided.set_num_threads(1)\n\n println(\"#################################################\")\n println(\"# order = \", order)\n println(\"# d = \", d)\n println(\"#################################################\")\n println()\n\n i(n) = Index(QN(0) => d, QN(1) => d; tags = \"i$n\")\n is = IndexSet(i, order ÷ 2)\n A = randomITensor(is'..., dag(is)...)\n B = randomITensor(is'..., dag(is)...)\n\n ITensors.enable_threaded_blocksparse(false)\n\n println(\"Serial contract:\")\n @disable_warn_order begin\n C_contract = @btime $A' * $B samples = 5\n end\n println()\n\n println(\"Threaded contract:\")\n @disable_warn_order begin\n ITensors.enable_threaded_blocksparse(true)\n C_threaded_contract = @btime $A' * $B samples = 5\n ITensors.enable_threaded_blocksparse(false)\n end\n println()\n @show C_contract ≈ C_threaded_contract\n return nothing\nend\n\nmain(d = 20, order = 4)","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"which outputs the following on a laptop with 6 threads, starting Julia with 5 threads:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"julia> main(d = 20, order = 4)\n#################################################\n# order = 4\n# d = 20\n#################################################\n\nThreads.nthreads() = 5\nSys.CPU_THREADS = 6\nBLAS.get_num_threads() = 1\nITensors.Strided.get_num_threads() = 1\n\nSerial contract:\n 21.558 ms (131 allocations: 7.34 MiB)\n\nThreaded contract:\n 5.934 ms (446 allocations: 7.37 MiB)\n\nC_contract ≈ C_threaded_contract = true","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"In addition, we plan to add more threading to other parts of the code beyond contraction (such as SVD) and improve composibility with other forms of threading like BLAS and Strided, so stay tuned!","category":"page"},{"location":"ProjMPOSum.html#ProjMPOSum","page":"ProjMPOSum","title":"ProjMPOSum","text":"","category":"section"},{"location":"ProjMPOSum.html#Description","page":"ProjMPOSum","title":"Description","text":"","category":"section"},{"location":"ProjMPOSum.html","page":"ProjMPOSum","title":"ProjMPOSum","text":"ProjMPOSum","category":"page"},{"location":"ProjMPOSum.html#ITensors.ITensorMPS.ProjMPOSum","page":"ProjMPOSum","title":"ITensors.ITensorMPS.ProjMPOSum","text":"A ProjMPOSum computes and stores the projection of an implied sum of MPOs into a basis defined by an MPS, leaving a certain number of site indices of each MPO unprojected. Which sites are unprojected can be shifted by calling the position! method. The MPOs used as input to a ProjMPOSum are not added together beforehand; instead when the product method of a ProjMPOSum is invoked, each projected MPO in the set of MPOs is multiplied by the input tensor one-by-one in an efficient way.\n\nDrawing of the network represented by a ProjMPOSum P([H1,H2,...]), showing the case of nsite(P)==2 and position!(P,psi,4) for an MPS psi (note the sum Σⱼ on the left):\n\n o--o--o- -o--o--o--o--o--o \n\n\n\n\n\n","category":"type"},{"location":"ProjMPOSum.html#Methods","page":"ProjMPOSum","title":"Methods","text":"","category":"section"},{"location":"ProjMPOSum.html","page":"ProjMPOSum","title":"ProjMPOSum","text":"product(::ProjMPOSum,::ITensor)\nposition!(::ProjMPOSum, ::MPS, ::Int)\nnoiseterm(::ProjMPOSum,::ITensor,::String)","category":"page"},{"location":"ProjMPOSum.html#ITensors.product-Tuple{ProjMPOSum, ITensor}","page":"ProjMPOSum","title":"ITensors.product","text":"product(P::ProjMPOSum,v::ITensor)\n\n(P::ProjMPOSum)(v::ITensor)\n\nEfficiently multiply the ProjMPOSum P by an ITensor v in the sense that the ProjMPOSum is a generalized square matrix or linear operator and v is a generalized vector in the space where it acts. The returned ITensor will have the same indices as v. The operator overload P(v) is shorthand for product(P,v).\n\n\n\n\n\n","category":"method"},{"location":"ProjMPOSum.html#ITensors.ITensorMPS.position!-Tuple{ProjMPOSum, MPS, Int64}","page":"ProjMPOSum","title":"ITensors.ITensorMPS.position!","text":"position!(P::ProjMPOSum, psi::MPS, pos::Int)\n\nGiven an MPS psi, shift the projection of the MPO represented by the ProjMPOSum P such that the set of unprojected sites begins with site pos. This operation efficiently reuses previous projections of the MPOs on sites that have already been projected. The MPS psi must have compatible bond indices with the previous projected MPO tensors for this operation to succeed.\n\n\n\n\n\n","category":"method"},{"location":"ProjMPOSum.html#ITensors.ITensorMPS.noiseterm-Tuple{ProjMPOSum, ITensor, String}","page":"ProjMPOSum","title":"ITensors.ITensorMPS.noiseterm","text":"noiseterm(P::ProjMPOSum,\n phi::ITensor,\n ortho::String)\n\nReturn a \"noise term\" or density matrix perturbation ITensor as proposed in Phys. Rev. B 72, 180403 for aiding convergence of DMRG calculations. The ITensor phi is the contracted product of MPS tensors acted on by the ProjMPOSum P, and ortho is a String which can take the values \"left\" or \"right\" depending on the sweeping direction of the DMRG calculation.\n\n\n\n\n\n","category":"method"},{"location":"ProjMPOSum.html#Properties","page":"ProjMPOSum","title":"Properties","text":"","category":"section"},{"location":"ProjMPOSum.html","page":"ProjMPOSum","title":"ProjMPOSum","text":"eltype(::ProjMPOSum)\nsize(::ProjMPOSum)","category":"page"},{"location":"ProjMPOSum.html#Base.eltype-Tuple{ProjMPOSum}","page":"ProjMPOSum","title":"Base.eltype","text":"eltype(P::ProjMPOSum)\n\nDeduce the element type (such as Float64 or ComplexF64) of the tensors in the ProjMPOSum P.\n\n\n\n\n\n","category":"method"},{"location":"ProjMPOSum.html#Base.size-Tuple{ProjMPOSum}","page":"ProjMPOSum","title":"Base.size","text":"size(P::ProjMPOSum)\n\nThe size of a ProjMPOSum are its dimensions (d,d) when viewed as a matrix or linear operator acting on a space of dimension d.\n\nFor example, if a ProjMPOSum maps from a space with indices (a,s1,s2,b) to the space (a',s1',s2',b') then the size is (d,d) where d = dim(a)*dim(s1)*dim(s1)*dim(b)\n\n\n\n\n\n","category":"method"},{"location":"SiteType.html#SiteType-and-op,-state,-val-functions","page":"SiteType and op, state, val functions","title":"SiteType and op, state, val functions","text":"","category":"section"},{"location":"SiteType.html#Description","page":"SiteType and op, state, val functions","title":"Description","text":"","category":"section"},{"location":"SiteType.html","page":"SiteType and op, state, val functions","title":"SiteType and op, state, val functions","text":"SiteType","category":"page"},{"location":"SiteType.html#ITensors.SiteTypes.SiteType","page":"SiteType and op, state, val functions","title":"ITensors.SiteTypes.SiteType","text":"SiteType is a parameterized type which allows making Index tags into Julia types. Use cases include overloading functions such as op, siteinds, and state which generate custom operators, Index arrays, and IndexVals associated with Index objects having a certain tag.\n\nTo make a SiteType type, you can use the string macro notation: SiteType\"MyTag\"\n\nTo make a SiteType value or object, you can use the notation: SiteType(\"MyTag\")\n\nThere are currently a few built-in site types recognized by jl. The system is easily extensible by users. To add new operators to an existing site type, or to create new site types, you can follow the instructions here.\n\nThe current built-in site types are:\n\nSiteType\"S=1/2\" (or SiteType\"S=½\")\nSiteType\"S=1\"\nSiteType\"Qubit\"\nSiteType\"Qudit\"\nSiteType\"Boson\"\nSiteType\"Fermion\"\nSiteType\"tJ\"\nSiteType\"Electron\"\n\nExamples\n\nTags on indices get turned into SiteTypes internally, and then we search for overloads of functions like op and siteind. For example:\n\njulia> s = siteind(\"S=1/2\")\n(dim=2|id=862|\"S=1/2,Site\")\n\njulia> @show op(\"Sz\", s);\nop(s, \"Sz\") = ITensor ord=2\nDim 1: (dim=2|id=862|\"S=1/2,Site\")'\nDim 2: (dim=2|id=862|\"S=1/2,Site\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 0.5 0.0\n 0.0 -0.5\n\njulia> @show op(\"Sx\", s);\nop(s, \"Sx\") = ITensor ord=2\nDim 1: (dim=2|id=862|\"S=1/2,Site\")'\nDim 2: (dim=2|id=862|\"S=1/2,Site\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 0.0 0.5\n 0.5 0.0\n\njulia> @show op(\"Sy\", s);\nop(s, \"Sy\") = ITensor ord=2\nDim 1: (dim=2|id=862|\"S=1/2,Site\")'\nDim 2: (dim=2|id=862|\"S=1/2,Site\")\nNDTensors.Dense{Complex{Float64},Array{Complex{Float64},1}}\n 2×2\n 0.0 + 0.0im -0.0 - 0.5im\n 0.0 + 0.5im 0.0 + 0.0im\n\njulia> s = siteind(\"Electron\")\n(dim=4|id=734|\"Electron,Site\")\n\njulia> @show op(\"Nup\", s);\nop(s, \"Nup\") = ITensor ord=2\nDim 1: (dim=4|id=734|\"Electron,Site\")'\nDim 2: (dim=4|id=734|\"Electron,Site\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n 4×4\n 0.0 0.0 0.0 0.0\n 0.0 1.0 0.0 0.0\n 0.0 0.0 0.0 0.0\n 0.0 0.0 0.0 1.0\n\nMany operators are available, for example:\n\nSiteType\"S=1/2\": \"Sz\", \"Sx\", \"Sy\", \"S+\", \"S-\", ...\nSiteType\"Electron\": \"Nup\", \"Ndn\", \"Nupdn\", \"Ntot\", \"Cup\", \"Cdagup\", \"Cdn\", \"Cdagdn\", \"Sz\", \"Sx\", \"Sy\", \"S+\", \"S-\", ...\n...\n\nYou can view the source code for the internal SiteType definitions and operators that are defined here.\n\n\n\n\n\n","category":"type"},{"location":"SiteType.html#Methods","page":"SiteType and op, state, val functions","title":"Methods","text":"","category":"section"},{"location":"SiteType.html","page":"SiteType and op, state, val functions","title":"SiteType and op, state, val functions","text":"op\nstate\nval\nspace","category":"page"},{"location":"SiteType.html#ITensors.SiteTypes.op","page":"SiteType and op, state, val functions","title":"ITensors.SiteTypes.op","text":"op(opname::String, s::Index; kwargs...)\n\nReturn an ITensor corresponding to the operator named opname for the Index s. The operator is constructed by calling an overload of either the op or op! methods which take a SiteType argument that corresponds to one of the tags of the Index s and an OpName\"opname\" argument that corresponds to the input operator name.\n\nOperator names can be combined using the \"*\" symbol, for example \"S+*S-\" or \"Sz*Sz*Sz\". The result is an ITensor made by forming each operator then contracting them together in a way corresponding to the usual operator product or matrix multiplication.\n\nThe op system is used by the OpSum system to convert operator names into ITensors, and can be used directly such as for applying operators to MPS.\n\nExample\n\ns = Index(2, \"Site,S=1/2\")\nSz = op(\"Sz\", s)\n\nTo see all of the operator names defined for the site types included with ITensor, please view the source code for each site type. Note that some site types such as \"S=1/2\" and \"Qubit\" are aliases for each other and share operator definitions.\n\n\n\n\n\nop(X::AbstractArray, s::Index...)\nop(M::Matrix, s::Index...)\n\nGiven a matrix M and a set of indices s,t,... return an operator ITensor with matrix elements given by M and indices s, s', t, t'\n\nExample\n\njulia> s = siteind(\"S=1/2\")\n(dim=2|id=575|\"S=1/2,Site\")\n\njulia> Sz = op([1/2 0; 0 -1/2],s)\nITensor ord=2 (dim=2|id=575|\"S=1/2,Site\")' (dim=2|id=575|\"S=1/2,Site\")\nNDTensors.Dense{Float64, Vector{Float64}}\n\njulia> @show Sz\nSz = ITensor ord=2\nDim 1: (dim=2|id=575|\"S=1/2,Site\")'\nDim 2: (dim=2|id=575|\"S=1/2,Site\")\nNDTensors.Dense{Float64, Vector{Float64}}\n 2×2\n 0.5 0.0\n 0.0 -0.5\nITensor ord=2 (dim=2|id=575|\"S=1/2,Site\")' (dim=2|id=575|\"S=1/2,Site\")\nNDTensors.Dense{Float64, Vector{Float64}}\n\n\n\n\n\nop(opname::String,sites::Vector{<:Index},n::Int; kwargs...)\n\nReturn an ITensor corresponding to the operator named opname for the n'th Index in the array sites.\n\nExample\n\ns = siteinds(\"S=1/2\", 4)\nSz2 = op(\"Sz\", s, 2)\n\n\n\n\n\n","category":"function"},{"location":"SiteType.html#ITensors.SiteTypes.state","page":"SiteType and op, state, val functions","title":"ITensors.SiteTypes.state","text":"state(s::Index, name::String; kwargs...)\n\nReturn an ITensor corresponding to the state named name for the Index s. The returned ITensor will have s as its only index.\n\nThe terminology here is based on the idea of a single-site state or wavefunction in physics.\n\nThe state function is implemented for various Index tags by overloading either the state or state! methods which take a SiteType argument corresponding to one of the tags of the Index s and an StateName\"name\" argument that corresponds to the input state name.\n\nThe state system is used by the MPS type to construct product-state MPS and for other purposes.\n\nExample\n\ns = Index(2, \"Site,S=1/2\")\nsup = state(s,\"Up\")\nsdn = state(s,\"Dn\")\nsxp = state(s,\"X+\")\nsxm = state(s,\"X-\")\n\n\n\n\n\n","category":"function"},{"location":"SiteType.html#ITensors.val","page":"SiteType and op, state, val functions","title":"ITensors.val","text":"val(q::QN,name)\n\nGet the value within the QN q corresponding to the string name\n\n\n\n\n\nval(s::Index, name::String)\n\nReturn an integer corresponding to the name of a certain value the Index s can take. In other words, the val function maps strings to specific integer values within the range 1:dim(s).\n\nThe val function is implemented for various Index tags by overloading methods named val which take a SiteType argument corresponding to one of the tags of the Index s and an ValName\"name\" argument that corresponds to the input name.\n\nExample\n\ns = Index(2, \"Site,S=1/2\")\nval(s,\"Up\") == 1\nval(s,\"Dn\") == 2\n\ns = Index(2, \"Site,Fermion\")\nval(s,\"Emp\") == 1\nval(s,\"Occ\") == 2\n\n\n\n\n\n","category":"function"},{"location":"SiteType.html#ITensors.space","page":"SiteType and op, state, val functions","title":"ITensors.space","text":"space(::SiteType\"Qubit\";\n conserve_qns = false,\n conserve_parity = conserve_qns,\n conserve_number = false,\n qnname_parity = \"Parity\",\n qnname_number = \"Number\")\n\nCreate the Hilbert space for a site of type \"Qubit\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\nspace(::SiteType\"S=1/2\";\n conserve_qns = false,\n conserve_sz = conserve_qns,\n conserve_szparity = false,\n qnname_sz = \"Sz\",\n qnname_szparity = \"SzParity\")\n\nCreate the Hilbert space for a site of type \"S=1/2\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\nspace(::SiteType\"S=1\";\n conserve_qns = false,\n conserve_sz = conserve_qns,\n qnname_sz = \"Sz\")\n\nCreate the Hilbert space for a site of type \"S=1\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\nspace(::SiteType\"Fermion\";\n conserve_qns=false,\n conserve_nf=conserve_qns,\n conserve_nfparity=conserve_qns,\n qnname_nf = \"Nf\",\n qnname_nfparity = \"NfParity\",\n qnname_sz = \"Sz\",\n conserve_sz = false)\n\nCreate the Hilbert space for a site of type \"Fermion\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\nspace(::SiteType\"Electron\";\n conserve_qns = false,\n conserve_sz = conserve_qns,\n conserve_nf = conserve_qns,\n conserve_nfparity = conserve_qns,\n qnname_sz = \"Sz\",\n qnname_nf = \"Nf\",\n qnname_nfparity = \"NfParity\")\n\nCreate the Hilbert space for a site of type \"Electron\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\nspace(::SiteType\"tJ\";\n conserve_qns = false,\n conserve_sz = conserve_qns,\n conserve_nf = conserve_qns,\n conserve_nfparity = conserve_qns,\n qnname_sz = \"Sz\",\n qnname_nf = \"Nf\",\n qnname_nfparity = \"NfParity\")\n\nCreate the Hilbert space for a site of type \"tJ\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\nspace(::SiteType\"Qudit\";\n dim = 2,\n conserve_qns = false,\n conserve_number = false,\n qnname_number = \"Number\")\n\nCreate the Hilbert space for a site of type \"Qudit\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\nspace(::SiteType\"Boson\";\n dim = 2,\n conserve_qns = false,\n conserve_number = false,\n qnname_number = \"Number\")\n\nCreate the Hilbert space for a site of type \"Boson\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\n","category":"function"},{"location":"Einsum.html#ITensor-Index-identity:-dimension-labels-and-Einstein-notation","page":"ITensor indices and Einstein notation","title":"ITensor Index identity: dimension labels and Einstein notation","text":"","category":"section"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"Many tensor contraction libraries use Einstein notation, such as NumPy's einsum function, ncon, and various Julia packages such as TensorOperations.jl, Tullio.jl, OMEinsum.jl, and Einsum.jl, among others.","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"ITensor also uses Einstein notation, however the labels are stored inside the tensor and carried around with them during various operations. In addition, the labels that determine if tensor indices match with each other, and therefore automatically contract when doing * or match when adding or subtracting, are more sophisticated than simple characters or strings. ITensor indices are given a unique random ID number when they are constructed, and additionally users can add additional information like prime levels and tags which uniquely determine an Index. This is in contrast to simpler implementations of the same idea, such as the NamedDims.jl package, which only allow symbols as the metadata for uniquely identifying a tensor/array dimension.","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"using ITensors\nusing Random\nRandom.seed!(1)","category":"page"},{"location":"Einsum.html#Index-identity","page":"ITensor indices and Einstein notation","title":"Index identity","text":"","category":"section"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"Here is an illustration of how the different types of Index metadata (random ID, prime level, and tags) work for Index identity:","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"i = Index(2)\nj = Index(2)\ni == j\nid(i)\nid(j)\nip = i'\nip == i\nplev(i) == 0\nplev(ip) == 1\nnoprime(ip) == i\nix = addtags(i, \"x\")\nix == i\nremovetags(ix, \"x\") == i\nixyz = addtags(ix, \"y,z\")\nixyz == addtags(i, \"z,y,x\")","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"The different metadata that are stored inside of ITensor indices that determine their identity are useful in different contexts. The random ID is particularly useful in the case when a new Index needs to be generated internally by ITensor, such as when performing a matrix factorization. In the case of a matrix factorization, we want to make sure that the new Index will not accidentally clash with an existing one, for example:","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"i = Index(2, \"i\")\nj = Index(2, \"j\")\nA = randomITensor(i, j)\nU, S, V = svd(A, i; lefttags=\"i\", righttags=\"j\");\ninds(U)\ninds(S)\ninds(V)\nnorm(U * S * V - A)","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"You can see that it would have been a problem here if there wasn't a new ID assigned to the Index, since it would have clashed with the original index. In this case, it could be avoided by giving the new indices different tags (with the keyword arguments lefttags and righttags), but in more complicated examples where it is not practical to do that (such as a case where many new indices are being introduced, for example for a tensor train (TT)/matrix product state (MPS)), it is convenient to not force users to come up with unique prime levels or tags themselves. It can also help to avoid accidental contractions in more complicated tensor network algorithms where there are many indices that can potentially have the same prime levels or tags.","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"In contrast, using multiple indices with the same Index ID but different prime levels and tags can be useful in situations where there is a more fundamental relationship between the spaces. For example, in the case of an ITensor corresponding to a Hermitian operator, it is helpful to make the bra space and ket spaces the same up to a prime level:","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"i = Index(2, \"i\")\nj = Index(3, \"j\")\nA = randomITensor(i', j', dag(i), dag(j))\nH = 0.5 * (A + swapprime(dag(A), 0 => 1))\nv = randomITensor(i, j)\nHv = noprime(H * v)\nvH = dag(v)' * H\nnorm(Hv - dag(vH))","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"Note that we have added dag in a few places, which is superfluous in this case since the tensors are real and dense but become important when the tensors are complex and/or have symmetries. You can see that in this case, it is very useful to relate the bra and ket spaces by prime levels, since it makes it much easier to perform operations that map from one space to another. We could have created A from 4 entirely different indices with different ID numbers, but it would make the operations a bit more cumbersome, as shown below:","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"i = Index(2, \"i\")\nj = Index(3, \"j\")\nip = Index(2, \"i\")\njp = Index(3, \"jp\")\nA = randomITensor(ip, jp, dag(i), dag(j))\nH = 0.5 * (A + swapinds(dag(A), (i, j), (ip, jp)))\nv = randomITensor(i, j)\nHv = replaceinds(H * v, (ip, jp) => (i, j))\nvH = replaceinds(dag(v), (i, j) => (ip, jp)) * H\nnorm(Hv - dag(vH))","category":"page"},{"location":"Einsum.html#Relationship-to-other-Einstein-notation-based-libraries","page":"ITensor indices and Einstein notation","title":"Relationship to other Einstein notation-based libraries","text":"","category":"section"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"Here we show examples of different ways to perform the contraction \"ab,bc,cd->ad\" in ITensor.","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"da, dc = 2, 3;\ndb, dd = da, dc;\ntags = (\"a\", \"b\", \"c\", \"d\");\ndims = (da, db, dc, dd);\na, b, c, d = Index.(dims, tags);\nAab = randomITensor(a, b)\nBbc = randomITensor(b, c)\nCcd = randomITensor(c, d)\n\n# \"ab,bc,cd->ad\"\nout1 = Aab * Bbc * Ccd\n@show hassameinds(out1, (a, d))\n\n#\n# Using replaceinds (most general way)\n#\n\n# \"ba,bc,dc->ad\"\nAba = replaceinds(Aab, (a, b) => (b, a))\nCdc = replaceinds(Ccd, (c, d) => (d, c))\nout2 = Aba * Bbc * Cdc\n@show hassameinds(out2, (a, d))\n\n#\n# Using setinds\n#\n\n# This is a bit lower level\n# since it doesn't check if the indices\n# are compatible in dimension,\n# so is not recommended in general.\nusing ITensors: setinds\n\nAba = setinds(Aab, (b, a))\nCdc = setinds(Ccd, (d, c))\nout2 = Aba * Bbc * Cdc\n@show hassameinds(out2, (a, d))\n\n#\n# Using prime levels (assuming\n# the indices were made with these\n# prime levels in the first place)\n#\n\na = Index(da, \"a\")\nc = Index(dc, \"c\")\nb, d = a', c'\nAab = randomITensor(a, b)\nBbc = randomITensor(b, c)\nCcd = randomITensor(c, d)\nout1 = Aab * Bbc * Ccd\n@show hassameinds(out1, (a, d))\n\nAba = swapprime(Aab, 0 => 1)\nCdc = swapprime(Ccd, 0 => 1)\nout2 = Aba * Bbc * Cdc\n@show hassameinds(out2, (a, d))\n\n#\n# Using tags (assuming\n# the indices were made with these\n# tags in the first place)\n#\n\na = Index(da, \"a\")\nc = Index(dc, \"c\")\nb, d = settags(a, \"b\"), settags(c, \"d\")\nAab = randomITensor(a, b)\nBbc = randomITensor(b, c)\nCcd = randomITensor(c, d)\nout1 = Aab * Bbc * Ccd\n@show hassameinds(out1, (a, d))\n\nAba = swaptags(Aab, \"a\", \"b\")\nCdc = swaptags(Ccd, \"c\", \"d\")\nout2 = Aba * Bbc * Cdc\n@show hassameinds(out2, (a, d))\n\n#\n# Using Julia Arrays\n#\n\nA = randn(da, db)\nB = randn(db, dc)\nC = randn(dc, dd)\n\ntags = (\"a\", \"b\", \"c\", \"d\")\ndims = (da, db, dc, dd)\na, b, c, d = Index.(dims, tags)\n\nAab = itensor(A, a, b)\nBbc = itensor(B, b, c)\nCcd = itensor(C, c, d)\nout1 = Aab * Bbc * Ccd\n@show hassameinds(out1, (a, d))\n\nAba = itensor(A, b, a)\nCdc = itensor(C, d, c)\nout2 = Aba * Bbc * Cdc\n@show hassameinds(out2, (a, d))\n\n#\n# Note that we may start allowing\n# this notation in future:\n# (https://github.com/ITensor/ITensors.jl/issues/673)\n#\n#out1 = A[a, b] * B[b, c] * C[c, d]\n#@show hassameinds(out1, (a, d))\n#\n#out2 = A[b, a] * B[b, c] * C[d, c]\n#@show hassameinds(out2, (a, d))","category":"page"},{"location":"ITensorType.html#ITensor","page":"ITensor","title":"ITensor","text":"","category":"section"},{"location":"ITensorType.html#Description","page":"ITensor","title":"Description","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"ITensor","category":"page"},{"location":"ITensorType.html#ITensors.ITensor","page":"ITensor","title":"ITensors.ITensor","text":"ITensor\n\nAn ITensor is a tensor whose interface is independent of its memory layout. Therefore it is not necessary to know the ordering of an ITensor's indices, only which indices an ITensor has. Operations like contraction and addition of ITensors automatically handle any memory permutations.\n\nExamples\n\njulia> i = Index(2, \"i\")\n(dim=2|id=287|\"i\")\n\n#\n# Make an ITensor with random elements:\n#\njulia> A = randomITensor(i', i)\nITensor ord=2 (dim=2|id=287|\"i\")' (dim=2|id=287|\"i\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=287|\"i\")'\nDim 2: (dim=2|id=287|\"i\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 0.28358594718392427 1.4342219756446355\n 1.6620103556283987 -0.40952231269251566\n\njulia> @show inds(A);\ninds(A) = ((dim=2|id=287|\"i\")', (dim=2|id=287|\"i\"))\n\n#\n# Set the i==1, i'==2 element to 1.0:\n#\njulia> A[i => 1, i' => 2] = 1;\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=287|\"i\")'\nDim 2: (dim=2|id=287|\"i\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 0.28358594718392427 1.4342219756446355\n 1.0 -0.40952231269251566\n\njulia> @show storage(A);\nstorage(A) = [0.28358594718392427, 1.0, 1.4342219756446355, -0.40952231269251566]\n\njulia> B = randomITensor(i, i');\n\njulia> @show B;\nB = ITensor ord=2\nDim 1: (dim=2|id=287|\"i\")\nDim 2: (dim=2|id=287|\"i\")'\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n -0.6510816500352691 0.2579101497658179\n 0.256266641521826 -0.9464735926768166\n\n#\n# Can add or subtract ITensors as long as they\n# have the same indices, in any order:\n#\njulia> @show A + B;\nA + B = ITensor ord=2\nDim 1: (dim=2|id=287|\"i\")'\nDim 2: (dim=2|id=287|\"i\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n -0.3674957028513448 1.6904886171664615\n 1.2579101497658178 -1.3559959053693322\n\n\n\n\n\n","category":"type"},{"location":"ITensorType.html#Dense-Constructors","page":"ITensor","title":"Dense Constructors","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"ITensor(::Type{<:Number}, ::ITensors.Indices)\nITensor(::Type{<:Number}, ::UndefInitializer, ::ITensors.Indices)\nITensor(::Type{<:Number}, ::Number, ::ITensors.Indices)\nITensor(::ITensors.AliasStyle, ::Type{<:Number}, ::Array{<:Number}, ::ITensors.Indices{Index{Int}}; kwargs...)\nrandomITensor(::Type{<:Number}, ::ITensors.Indices)\nonehot","category":"page"},{"location":"ITensorType.html#ITensors.ITensor-Tuple{Type{<:Number}, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.ITensor","text":"ITensor([::Type{ElT} = Float64, ]inds)\nITensor([::Type{ElT} = Float64, ]inds::Index...)\n\nConstruct an ITensor filled with zeros having indices inds and element type ElT. If the element type is not specified, it defaults to Float64.\n\nThe storage will have NDTensors.Dense type.\n\nExamples\n\ni = Index(2,\"index_i\")\nj = Index(4,\"index_j\")\nk = Index(3,\"index_k\")\n\nA = ITensor(i,j)\nB = ITensor(ComplexF64,k,j)\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.ITensor-Tuple{Type{<:Number}, UndefInitializer, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.ITensor","text":"ITensor([::Type{ElT} = Float64, ]::UndefInitializer, inds)\nITensor([::Type{ElT} = Float64, ]::UndefInitializer, inds::Index...)\n\nConstruct an ITensor filled with undefined elements having indices inds and element type ElT. If the element type is not specified, it defaults to Float64. One purpose for using this constructor is that initializing the elements in an undefined way is faster than initializing them to a set value such as zero.\n\nThe storage will have NDTensors.Dense type.\n\nExamples\n\ni = Index(2,\"index_i\")\nj = Index(4,\"index_j\")\nk = Index(3,\"index_k\")\n\nA = ITensor(undef,i,j)\nB = ITensor(ComplexF64,undef,k,j)\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.ITensor-Tuple{Type{<:Number}, Number, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.ITensor","text":"ITensor([ElT::Type, ]x::Number, inds)\nITensor([ElT::Type, ]x::Number, inds::Index...)\n\nConstruct an ITensor with all elements set to x and indices inds.\n\nIf x isa Int or x isa Complex{Int} then the elements will be set to float(x) unless specified otherwise by the first input.\n\nThe storage will have NDTensors.Dense type.\n\nExamples\n\n```julia i = Index(2,\"indexi\"); j = Index(4,\"indexj\"); k = Index(3,\"index_k\");\n\nA = ITensor(1.0, i, j) A = ITensor(1, i, j) # same as above B = ITensor(2.0+3.0im, j, k) ```\n\n!!! warning In future versions this may not automatically convert integer inputs with float, and in that case the particular element type should not be relied on.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.ITensor-Tuple{NDTensors.AliasStyle, Type{<:Number}, Array{<:Number}, Union{Tuple{Vararg{Index{Int64}}}, Vector{Index{Int64}}}}","page":"ITensor","title":"ITensors.ITensor","text":"ITensor([ElT::Type, ]A::Array, inds)\nITensor([ElT::Type, ]A::Array, inds::Index...)\n\nitensor([ElT::Type, ]A::Array, inds)\nitensor([ElT::Type, ]A::Array, inds::Index...)\n\nConstruct an ITensor from an Array A and indices inds. The ITensor will be a view of the Array data if possible (if no conversion to a different element type is necessary).\n\nIf specified, the ITensor will have element type ElT.\n\nIf the element type of A is Int or Complex{Int} and the desired element type isn't specified, it will be converted to Float64 or Complex{Float64} automatically. To keep the element type as an integer, specify it explicitly, for example with:\n\ni = Index(2, \"i\")\nA = [0 1; 1 0]\nT = ITensor(eltype(A), A, i', dag(i))\n\nExamples\n\ni = Index(2,\"index_i\")\nj = Index(2,\"index_j\")\n\nM = [1. 2;\n 3 4]\nT = ITensor(M, i, j)\nT[i => 1, j => 1] = 3.3\nM[1, 1] == 3.3\nT[i => 1, j => 1] == 3.3\n\nwarning: Warning\nIn future versions this may not automatically convert Int/Complex{Int} inputs to floating point versions with float (once tensor operations using Int/Complex{Int} are natively as fast as floating point operations), and in that case the particular element type should not be relied on. To avoid extra conversions (and therefore allocations) it is best practice to directly construct with itensor([0. 1; 1 0], i', dag(i)) if you want a floating point element type. The conversion is done as a performance optimization since often tensors are passed to BLAS/LAPACK and need to be converted to floating point types compatible with those libraries, but future projects in Julia may allow for efficient operations with more general element types (for example see https://github.com/JuliaLinearAlgebra/Octavian.jl).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.randomITensor-Tuple{Type{<:Number}, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.randomITensor","text":"randomITensor([::Type{ElT <: Number} = Float64, ]inds)\nrandomITensor([::Type{ElT <: Number} = Float64, ]inds::Index...)\n\nConstruct an ITensor with type ElT and indices inds, whose elements are normally distributed random numbers. If the element type is not specified, it defaults to Float64.\n\nExamples\n\ni = Index(2,\"index_i\")\nj = Index(4,\"index_j\")\nk = Index(3,\"index_k\")\n\nA = randomITensor(i,j)\nB = randomITensor(ComplexF64,undef,k,j)\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.onehot","page":"ITensor","title":"ITensors.onehot","text":"onehot(ivs...)\nsetelt(ivs...)\nonehot(::Type, ivs...)\nsetelt(::Type, ivs...)\n\nCreate an ITensor with all zeros except the specified value, which is set to 1.\n\nExamples\n\ni = Index(2,\"i\")\nA = onehot(i=>2)\n# A[i=>2] == 1, all other elements zero\n\n# Specify the element type\nA = onehot(Float32, i=>2)\n\nj = Index(3,\"j\")\nB = onehot(i=>1,j=>3)\n# B[i=>1,j=>3] == 1, all other element zero\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#Dense-View-Constructors","page":"ITensor","title":"Dense View Constructors","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"itensor(::Array{<:Number}, ::ITensors.Indices)","category":"page"},{"location":"ITensorType.html#ITensors.itensor-Tuple{Array{<:Number}, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.itensor","text":"itensor(args...; kwargs...)\n\nLike the ITensor constructor, but with attempt to make a view of the input data when possible.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#QN-BlockSparse-Constructors","page":"ITensor","title":"QN BlockSparse Constructors","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"ITensor(::Type{<:Number}, ::QN, ::ITensors.QNIndices)\nITensor(::ITensors.AliasStyle, ::Type{<:Number}, ::Array{<:Number}, ::ITensors.QNIndices; tol=0)\nITensor(::Type{<:Number}, ::UndefInitializer, ::QN, ::ITensors.Indices)","category":"page"},{"location":"ITensorType.html#ITensors.ITensor-Tuple{Type{<:Number}, QN, Union{Tuple{Vararg{Index{Vector{Pair{QN, Int64}}}}}, Vector{Index{Vector{Pair{QN, Int64}}}}}}","page":"ITensor","title":"ITensors.ITensor","text":"ITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]inds)\nITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]inds::Index...)\n\nConstruct an ITensor with BlockSparse storage filled with zero(ElT) where the nonzero blocks are determined by flux.\n\nIf ElT is not specified it defaults to Float64.\n\nIf flux is not specified, the ITensor will be empty (it will contain no blocks, and have an undefined flux). The flux will be set by the first element that is set.\n\nExamples\n\njulia> i\n(dim=3|id=212|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\n\njulia> @show ITensor(QN(0), i', dag(i));\nITensor(QN(0), i', dag(i)) = ITensor ord=2\nDim 1: (dim=3|id=212|\"i\")' \n 1: QN(0) => 1\n 2: QN(1) => 2\nDim 2: (dim=3|id=212|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\nNDTensors.BlockSparse{Float64, Vector{Float64}, 2}\n 3×3\nBlock(1, 1)\n [1:1, 1:1]\n 0.0\n\nBlock(2, 2)\n [2:3, 2:3]\n 0.0 0.0\n 0.0 0.0\n\njulia> @show ITensor(QN(1), i', dag(i));\nITensor(QN(1), i', dag(i)) = ITensor ord=2\nDim 1: (dim=3|id=212|\"i\")' \n 1: QN(0) => 1\n 2: QN(1) => 2\nDim 2: (dim=3|id=212|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\nNDTensors.BlockSparse{Float64, Vector{Float64}, 2}\n 3×3\nBlock(2, 1)\n [2:3, 1:1]\n 0.0\n 0.0\n\njulia> @show ITensor(ComplexF64, QN(1), i', dag(i));\nITensor(ComplexF64, QN(1), i', dag(i)) = ITensor ord=2\nDim 1: (dim=3|id=212|\"i\")' \n 1: QN(0) => 1\n 2: QN(1) => 2\nDim 2: (dim=3|id=212|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\nNDTensors.BlockSparse{ComplexF64, Vector{ComplexF64}, 2}\n 3×3\nBlock(2, 1)\n [2:3, 1:1]\n 0.0 + 0.0im\n 0.0 + 0.0im\n\njulia> @show ITensor(undef, QN(1), i', dag(i));\nITensor(undef, QN(1), i', dag(i)) = ITensor ord=2\nDim 1: (dim=3|id=212|\"i\")' \n 1: QN(0) => 1\n 2: QN(1) => 2\nDim 2: (dim=3|id=212|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\nNDTensors.BlockSparse{Float64, Vector{Float64}, 2}\n 3×3\nBlock(2, 1)\n [2:3, 1:1]\n 0.0\n 1.63e-322\n\nConstruction with undefined flux:\n\njulia> A = ITensor(i', dag(i));\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=3|id=212|\"i\")' \n 1: QN(0) => 1\n 2: QN(1) => 2\nDim 2: (dim=3|id=212|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\nNDTensors.EmptyStorage{NDTensors.EmptyNumber, NDTensors.BlockSparse{NDTensors.EmptyNumber, Vector{NDTensors.EmptyNumber}, 2}}\n 3×3\n\n\n\njulia> isnothing(flux(A))\ntrue\n\njulia> A[i' => 1, i => 2] = 2\n2\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=3|id=212|\"i\")' \n 1: QN(0) => 1\n 2: QN(1) => 2\nDim 2: (dim=3|id=212|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\nNDTensors.BlockSparse{Int64, Vector{Int64}, 2}\n 3×3\nBlock(1, 2)\n [1:1, 2:3]\n 2 0\n\njulia> flux(A)\nQN(-1)\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.ITensor-Tuple{NDTensors.AliasStyle, Type{<:Number}, Array{<:Number}, Union{Tuple{Vararg{Index{Vector{Pair{QN, Int64}}}}}, Vector{Index{Vector{Pair{QN, Int64}}}}}}","page":"ITensor","title":"ITensors.ITensor","text":"ITensor([ElT::Type, ]A::Array, inds)\nITensor([ElT::Type, ]A::Array, inds::Index...)\n\nitensor([ElT::Type, ]A::Array, inds)\nitensor([ElT::Type, ]A::Array, inds::Index...)\n\nConstruct an ITensor from an Array A and indices inds. The ITensor will be a view of the Array data if possible (if no conversion to a different element type is necessary).\n\nIf specified, the ITensor will have element type ElT.\n\nIf the element type of A is Int or Complex{Int} and the desired element type isn't specified, it will be converted to Float64 or Complex{Float64} automatically. To keep the element type as an integer, specify it explicitly, for example with:\n\ni = Index(2, \"i\")\nA = [0 1; 1 0]\nT = ITensor(eltype(A), A, i', dag(i))\n\nExamples\n\ni = Index(2,\"index_i\")\nj = Index(2,\"index_j\")\n\nM = [1. 2;\n 3 4]\nT = ITensor(M, i, j)\nT[i => 1, j => 1] = 3.3\nM[1, 1] == 3.3\nT[i => 1, j => 1] == 3.3\n\nwarning: Warning\nIn future versions this may not automatically convert Int/Complex{Int} inputs to floating point versions with float (once tensor operations using Int/Complex{Int} are natively as fast as floating point operations), and in that case the particular element type should not be relied on. To avoid extra conversions (and therefore allocations) it is best practice to directly construct with itensor([0. 1; 1 0], i', dag(i)) if you want a floating point element type. The conversion is done as a performance optimization since often tensors are passed to BLAS/LAPACK and need to be converted to floating point types compatible with those libraries, but future projects in Julia may allow for efficient operations with more general element types (for example see https://github.com/JuliaLinearAlgebra/Octavian.jl).\n\n\n\n\n\nITensor([ElT::Type, ]::AbstractArray, inds; tol=0.0, checkflux=true)\n\nCreate a block sparse ITensor from the input Array, and collection of QN indices. Zeros are dropped and nonzero blocks are determined from the zero values of the array.\n\nOptionally, you can set a tolerance such that elements less than or equal to the tolerance are dropped.\n\nBy default, this will check that the flux of the nonzero blocks are consistent with each other. You can disable this check by setting checkflux=false.\n\nExamples\n\njulia> i = Index([QN(0)=>1, QN(1)=>2], \"i\");\n\njulia> A = [1e-9 0.0 0.0;\n 0.0 2.0 3.0;\n 0.0 1e-10 4.0];\n\njulia> @show ITensor(A, i', dag(i); tol = 1e-8);\nITensor(A, i', dag(i); tol = 1.0e-8) = ITensor ord=2\nDim 1: (dim=3|id=468|\"i\")' \n 1: QN(0) => 1\n 2: QN(1) => 2\nDim 2: (dim=3|id=468|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\nNDTensors.BlockSparse{Float64,Array{Float64,1},2}\n 3×3\nBlock: (2, 2)\n [2:3, 2:3]\n 2.0 3.0\n 0.0 4.0\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.ITensor-Tuple{Type{<:Number}, UndefInitializer, QN, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.ITensor","text":"ITensor([::Type{ElT} = Float64,] ::UndefInitializer, flux::QN, inds)\nITensor([::Type{ElT} = Float64,] ::UndefInitializer, flux::QN, inds::Index...)\n\nConstruct an ITensor with indices inds and BlockSparse storage with undefined elements of type ElT, where the nonzero (allocated) blocks are determined by the provided QN flux. One purpose for using this constructor is that initializing the elements in an undefined way is faster than initializing them to a set value such as zero.\n\nThe storage will have NDTensors.BlockSparse type.\n\nExamples\n\ni = Index([QN(0)=>1, QN(1)=>2], \"i\")\nA = ITensor(undef,QN(0),i',dag(i))\nB = ITensor(Float64,undef,QN(0),i',dag(i))\nC = ITensor(ComplexF64,undef,QN(0),i',dag(i))\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Diagonal-constructors","page":"ITensor","title":"Diagonal constructors","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"diagITensor(::Type{<:Number}, ::ITensors.Indices)\ndiagITensor(::ITensors.AliasStyle, ::Type{<:Number}, ::Vector{<:Number}, ::ITensors.Indices)\ndiagITensor(::ITensors.AliasStyle, ::Type{<:Number}, ::Number, ::ITensors.Indices)\ndelta(::Type{<:Number}, ::ITensors.Indices)","category":"page"},{"location":"ITensorType.html#ITensors.diagITensor-Tuple{Type{<:Number}, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.diagITensor","text":"diagITensor([::Type{ElT} = Float64, ]inds)\ndiagITensor([::Type{ElT} = Float64, ]inds::Index...)\n\nMake a sparse ITensor of element type ElT with only elements along the diagonal stored. Defaults to having zero(T) along the diagonal.\n\nThe storage will have NDTensors.Diag type.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.diagITensor-Tuple{NDTensors.AliasStyle, Type{<:Number}, Vector{<:Number}, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.diagITensor","text":"diagITensor([ElT::Type, ]v::Vector, inds...)\ndiagitensor([ElT::Type, ]v::Vector, inds...)\n\nMake a sparse ITensor with non-zero elements only along the diagonal. In general, the diagonal elements will be those stored in v and the ITensor will have element type eltype(v), unless specified explicitly by ElT. The storage will have NDTensors.Diag type.\n\nIn the case when eltype(v) isa Union{Int, Complex{Int}}, by default it will be converted to float(v). Note that this behavior is subject to change in the future.\n\nThe version diagITensor will never output an ITensor whose storage data is an alias of the input vector data.\n\nThe version diagitensor might output an ITensor whose storage data is an alias of the input vector data in order to minimize operations.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.diagITensor-Tuple{NDTensors.AliasStyle, Type{<:Number}, Number, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.diagITensor","text":"diagITensor([ElT::Type, ]x::Number, inds...)\ndiagitensor([ElT::Type, ]x::Number, inds...)\n\nMake a sparse ITensor with non-zero elements only along the diagonal. In general, the diagonal elements will be set to the value x and the ITensor will have element type eltype(x), unless specified explicitly by ElT. The storage will have NDTensors.Diag type.\n\nIn the case when x isa Union{Int, Complex{Int}}, by default it will be converted to float(x). Note that this behavior is subject to change in the future.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.delta-Tuple{Type{<:Number}, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.delta","text":"delta([::Type{ElT} = Float64, ]inds)\ndelta([::Type{ElT} = Float64, ]inds::Index...)\n\nMake a uniform diagonal ITensor with all diagonal elements one(ElT). Only a single diagonal element is stored.\n\nThis function has an alias δ.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#QN-Diagonal-constructors","page":"ITensor","title":"QN Diagonal constructors","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"diagITensor(::Type{<:Number}, ::QN, ::ITensors.Indices)\ndelta(::Type{<:Number}, ::QN, ::ITensors.Indices)","category":"page"},{"location":"ITensorType.html#ITensors.diagITensor-Tuple{Type{<:Number}, QN, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.diagITensor","text":"diagITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]is)\ndiagITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]is::Index...)\n\nMake an ITensor with storage type NDTensors.DiagBlockSparse with elements zero(ElT). The ITensor only has diagonal blocks consistent with the specified flux.\n\nIf the element type is not specified, it defaults to Float64. If theflux is not specified, it defaults to QN().\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.delta-Tuple{Type{<:Number}, QN, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.delta","text":"delta([::Type{ElT} = Float64, ][flux::QN = QN(), ]is)\ndelta([::Type{ElT} = Float64, ][flux::QN = QN(), ]is::Index...)\n\nMake an ITensor with storage type NDTensors.DiagBlockSparse with uniform elements one(ElT). The ITensor only has diagonal blocks consistent with the specified flux.\n\nIf the element type is not specified, it defaults to Float64. If theflux is not specified, it defaults to QN().\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Convert-to-Array","page":"ITensor","title":"Convert to Array","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"Array{ElT, N}(::ITensor, ::ITensors.Indices) where {ElT, N}\narray(::ITensor, ::Any...)\nmatrix(::ITensor, ::Any...)\nvector(::ITensor, ::Any...)\narray(::ITensor)\nmatrix(::ITensor)\nvector(::ITensor)","category":"page"},{"location":"ITensorType.html#Core.Array-Union{Tuple{N}, Tuple{ElT}, Tuple{ITensor, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}} where {ElT, N}","page":"ITensor","title":"Core.Array","text":"Array{ElT, N}(T::ITensor, i:Index...)\nArray{ElT}(T::ITensor, i:Index...)\nArray(T::ITensor, i:Index...)\n\nMatrix{ElT}(T::ITensor, row_i:Index, col_i::Index)\nMatrix(T::ITensor, row_i:Index, col_i::Index)\n\nVector{ElT}(T::ITensor)\nVector(T::ITensor)\n\nGiven an ITensor T with indices i..., returns an Array with a copy of the ITensor's elements. The order in which the indices are provided indicates the order of the data in the resulting Array.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.array-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"NDTensors.array","text":"array(T::ITensor, inds...)\n\nConvert an ITensor T to an Array.\n\nThe ordering of the elements in the Array are specified by the input indices inds. This tries to avoid copying of possible (i.e. may return a view of the original data), for example if the ITensor's storage is Dense and the indices are already in the specified ordering so that no permutation is required.\n\nwarning: Warning\nNote that in the future we may return specialized AbstractArray types for certain storage types, for example a LinearAlgebra.Diagonal type for an ITensor with Diag storage. The specific storage type shouldn't be relied upon.\n\nSee also matrix, vector.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.matrix-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"NDTensors.matrix","text":"matrix(T::ITensor, inds...)\n\nConvert an ITensor T to a Matrix.\n\nThe ordering of the elements in the Matrix are specified by the input indices inds. This tries to avoid copying of possible (i.e. may return a view of the original data), for example if the ITensor's storage is Dense and the indices are already in the specified ordering so that no permutation is required.\n\nwarning: Warning\nNote that in the future we may return specialized AbstractArray types for certain storage types, for example a LinearAlgebra.Diagonal type for an ITensor with Diag storage. The specific storage type shouldn't be relied upon.\n\nSee also array, vector.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.vector-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"NDTensors.vector","text":"vector(T::ITensor, inds...)\n\nConvert an ITensor T to an Vector.\n\nThe ordering of the elements in the Array are specified by the input indices inds. This tries to avoid copying of possible (i.e. may return a view of the original data), for example if the ITensor's storage is Dense and the indices are already in the specified ordering so that no permutation is required.\n\nwarning: Warning\nNote that in the future we may return specialized AbstractArray types for certain storage types, for example a LinearAlgebra.Diagonal type for an ITensor with Diag storage. The specific storage type shouldn't be relied upon.\n\nSee also array, matrix.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.array-Tuple{ITensor}","page":"ITensor","title":"NDTensors.array","text":"array(T::ITensor)\n\nGiven an ITensor T, returns an Array with a copy of the ITensor's elements, or a view in the case the the ITensor's storage is Dense.\n\nThe ordering of the elements in the Array, in terms of which Index is treated as the row versus column, depends on the internal layout of the ITensor.\n\nwarning: Warning\nThis method is intended for developer use only and not recommended for use in ITensor applications unless you know what you are doing (for example you are certain of the memory ordering of the ITensor because you permuted the indices into a certain order).\n\nSee also matrix, vector.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.matrix-Tuple{ITensor}","page":"ITensor","title":"NDTensors.matrix","text":"matrix(T::ITensor)\n\nGiven an ITensor T with two indices, returns a Matrix with a copy of the ITensor's elements, or a view in the case the ITensor's storage is Dense.\n\nThe ordering of the elements in the Matrix, in terms of which Index is treated as the row versus column, depends on the internal layout of the ITensor.\n\nwarning: Warning\nThis method is intended for developer use only and not recommended for use in ITensor applications unless you know what you are doing (for example you are certain of the memory ordering of the ITensor because you permuted the indices into a certain order).\n\nSee also array, vector.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.vector-Tuple{ITensor}","page":"ITensor","title":"NDTensors.vector","text":"vector(T::ITensor)\n\nGiven an ITensor T with one index, returns a Vector with a copy of the ITensor's elements, or a view in the case the ITensor's storage is Dense.\n\nSee also array, matrix.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Getting-and-setting-elements","page":"ITensor","title":"Getting and setting elements","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"getindex(::ITensor, ::Any...)\nsetindex!(::ITensor, ::Number, ::Int...)","category":"page"},{"location":"ITensorType.html#Base.getindex-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"Base.getindex","text":"getindex(T::ITensor, ivs...)\n\nGet the specified element of the ITensor, using a list of IndexVals or Pair{<:Index, Int}.\n\nExample\n\ni = Index(2; tags = \"i\")\nA = ITensor(2.0, i, i')\nA[i => 1, i' => 2] # 2.0, same as: A[i' => 2, i => 1]\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Base.setindex!-Tuple{ITensor, Number, Vararg{Int64}}","page":"ITensor","title":"Base.setindex!","text":"setindex!(T::ITensor, x::Number, ivs...)\n\nsetindex!(T::ITensor, x::Number, I::Integer...)\n\nsetindex!(T::ITensor, x::Number, I::CartesianIndex)\n\nSet the specified element of the ITensor, using a list of Pair{<:Index, Integer} (or IndexVal).\n\nIf just integers are used, set the specified element of the ITensor using internal Index ordering of the ITensor (only for advanced usage, only use if you know the axact ordering of the indices).\n\nExample\n\ni = Index(2; tags = \"i\")\nA = ITensor(i, i')\nA[i => 1, i' => 2] = 1.0 # same as: A[i' => 2, i => 1] = 1.0\nA[1, 2] = 1.0 # same as: A[i => 1, i' => 2] = 1.0\n\n# Some simple slicing is also supported\nA[i => 2, i' => :] = [2.0 3.0]\nA[2, :] = [2.0 3.0]\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Properties","page":"ITensor","title":"Properties","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"inds(::ITensor)\nind(::ITensor, ::Int)\ndir(::ITensor, ::Index)","category":"page"},{"location":"ITensorType.html#NDTensors.inds-Tuple{ITensor}","page":"ITensor","title":"NDTensors.inds","text":"inds(T::ITensor)\n\nReturn the indices of the ITensor as a Tuple.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.ind-Tuple{ITensor, Int64}","page":"ITensor","title":"NDTensors.ind","text":"ind(T::ITensor, i::Int)\n\nGet the Index of the ITensor along dimension i.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.dir-Tuple{ITensor, Index}","page":"ITensor","title":"ITensors.dir","text":"dir(A::ITensor, i::Index)\n\nReturn the direction of the Index i in the ITensor A.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Priming_and_tagging_ITensor","page":"ITensor","title":"Priming and tagging","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"prime(::ITensor, ::Any...)\nsetprime(::ITensor, ::Any...)\nnoprime(::ITensor, ::Any...)\nmapprime(::ITensor, ::Any...)\nswapprime(::ITensor, ::Any...)\naddtags(::ITensor, ::Any...)\nremovetags(::ITensor, ::Any...)\nreplacetags(::ITensor, ::Any...)\nsettags(::ITensor, ::Any...)\nswaptags(::ITensor, ::Any...)","category":"page"},{"location":"ITensorType.html#ITensors.prime-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.prime","text":"prime[!](A::ITensor, plinc::Int = 1; ) -> ITensor\n\nprime(inds, plinc::Int = 1; ) -> IndexSet\n\nIncrease the prime level of the indices of an ITensor or collection of indices.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.setprime-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.setprime","text":"setprime[!](A::ITensor, plev::Int; ) -> ITensor\n\nsetprime(inds, plev::Int; ) -> IndexSet\n\nSet the prime level of the indices of an ITensor or collection of indices.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.noprime-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.noprime","text":"noprime[!](A::ITensor; ) -> ITensor\n\nnoprime(inds; ) -> IndexSet\n\nSet the prime level of the indices of an ITensor or collection of indices to zero.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.mapprime-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.mapprime","text":"replaceprime[!](A::ITensor, plold::Int, plnew::Int; ) -> ITensor\nreplaceprime[!](A::ITensor, plold => plnew; ) -> ITensor\nmapprime[!](A::ITensor, ; ) -> ITensor\n\nreplaceprime(inds, plold::Int, plnew::Int; )\nreplaceprime(inds::IndexSet, plold => plnew; )\nmapprime(inds, ; )\n\nSet the prime level of the indices of an ITensor or collection of indices with prime level plold to plnew.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.swapprime-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.swapprime","text":"swapprime[!](A::ITensor, pl1::Int, pl2::Int; ) -> ITensor\nswapprime[!](A::ITensor, pl1 => pl2; ) -> ITensor\n\nswapprime(inds, pl1::Int, pl2::Int; )\nswapprime(inds, pl1 => pl2; )\n\nSet the prime level of the indices of an ITensor or collection of indices with prime level pl1 to pl2, and those with prime level pl2 to pl1.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.TagSets.addtags-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.TagSets.addtags","text":"addtags[!](A::ITensor, ts::String; ) -> ITensor\n\naddtags(inds, ts::String; )\n\nAdd the tags ts to the indices of an ITensor or collection of indices.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.TagSets.removetags-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.TagSets.removetags","text":"removetags[!](A::ITensor, ts::String; ) -> ITensor\n\nremovetags(inds, ts::String; )\n\nRemove the tags ts from the indices of an ITensor or collection of indices.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.TagSets.replacetags-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.TagSets.replacetags","text":"replacetags[!](A::ITensor, tsold::String, tsnew::String; ) -> ITensor\n\nreplacetags(is::IndexSet, tsold::String, tsnew::String; ) -> IndexSet\n\nReplace the tags tsold with tsnew for the indices of an ITensor.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.settags-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.settags","text":"settags[!](A::ITensor, ts::String; ) -> ITensor\n\nsettags(is::IndexSet, ts::String; ) -> IndexSet\n\nSet the tags of the indices of an ITensor or IndexSet to ts.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.swaptags-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.swaptags","text":"swaptags[!](A::ITensor, ts1::String, ts2::String; ) -> ITensor\n\nswaptags(is::IndexSet, ts1::String, ts2::String; ) -> IndexSet\n\nSwap the tags ts1 with ts2 for the indices of an ITensor.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Index-collections-set-operations","page":"ITensor","title":"Index collections set operations","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"commoninds\ncommonind\nuniqueinds\nuniqueind\nnoncommoninds\nnoncommonind\nunioninds\nunionind\nhascommoninds","category":"page"},{"location":"ITensorType.html#ITensors.commoninds","page":"ITensor","title":"ITensors.commoninds","text":"commoninds(A, B; kwargs...)\n\nReturn a Vector with indices that are common between the indices of A and B (the set intersection, similar to Base.intersect).\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.commonind","page":"ITensor","title":"ITensors.commonind","text":"commonind(A, B; kwargs...)\n\nReturn the first Index common between the indices of A and B.\n\nSee also commoninds.\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.uniqueinds","page":"ITensor","title":"ITensors.uniqueinds","text":"uniqueinds(A, B; kwargs...)\n\nReturn Vector with indices that are unique to the set of indices of A and not in B (the set difference, similar to Base.setdiff).\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.uniqueind","page":"ITensor","title":"ITensors.uniqueind","text":"uniqueind(A, B; kwargs...)\n\nReturn the first Index unique to the set of indices of A and not in B.\n\nSee also uniqueinds.\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.noncommoninds","page":"ITensor","title":"ITensors.noncommoninds","text":"noncommoninds(A, B; kwargs...)\n\nReturn a Vector with indices that are not common between the indices of A and B (the symmetric set difference, similar to Base.symdiff).\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.noncommonind","page":"ITensor","title":"ITensors.noncommonind","text":"noncommonind(A, B; kwargs...)\n\nReturn the first Index not common between the indices of A and B.\n\nSee also noncommoninds.\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.unioninds","page":"ITensor","title":"ITensors.unioninds","text":"unioninds(A, B; kwargs...)\n\nReturn a Vector with indices that are the union of the indices of A and B (the set union, similar to Base.union).\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.unionind","page":"ITensor","title":"ITensors.unionind","text":"unionind(A, B; kwargs...)\n\nReturn the first Index in the union of the indices of A and B.\n\nSee also unioninds.\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.hascommoninds","page":"ITensor","title":"ITensors.hascommoninds","text":"hascommoninds(A, B; kwargs...)\n\nhascommoninds(B; kwargs...) -> f::Function\n\nCheck if the ITensors or sets of indices A and B have common indices.\n\nIf only one ITensor or set of indices B is passed, return a function f such that f(A) = hascommoninds(A, B; kwargs...)\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#Index-Manipulations","page":"ITensor","title":"Index Manipulations","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"replaceind(::ITensor, ::Any...)\nreplaceinds(::ITensor, ::Any...)\nswapind(::ITensor, ::Any...)\nswapinds(::ITensor, ::Any...)","category":"page"},{"location":"ITensorType.html#ITensors.replaceind-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.replaceind","text":"replaceind[!](A::ITensor, i1::Index, i2::Index) -> ITensor\n\nReplace the Index i1 with the Index i2 in the ITensor.\n\nThe indices must have the same space (i.e. the same dimension and QNs, if applicable).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.replaceinds-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.replaceinds","text":"replaceinds(A::ITensor, inds1, inds2) -> ITensor\n\nreplaceinds!(A::ITensor, inds1, inds2)\n\nReplace the Index inds1[n] with the Index inds2[n] in the ITensor, where n runs from 1 to length(inds1) == length(inds2).\n\nThe indices must have the same space (i.e. the same dimension and QNs, if applicable).\n\nThe storage of the ITensor is not modified or copied (the output ITensor is a view of the input ITensor).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.swapind-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.swapind","text":"swapind(A::ITensor, i1::Index, i2::Index) -> ITensor\n\nswapind!(A::ITensor, i1::Index, i2::Index)\n\nSwap the Index i1 with the Index i2 in the ITensor.\n\nThe indices must have the same space (i.e. the same dimension and QNs, if applicable).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.swapinds-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.swapinds","text":"swapinds(A::ITensor, inds1, inds2) -> ITensor\n\nswapinds!(A::ITensor, inds1, inds2)\n\nSwap the Index inds1[n] with the Index inds2[n] in the ITensor, where n runs from 1 to length(inds1) == length(inds2).\n\nThe indices must have the same space (i.e. the same dimension and QNs, if applicable).\n\nThe storage of the ITensor is not modified or copied (the output ITensor is a view of the input ITensor).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Math-operations","page":"ITensor","title":"Math operations","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"*(::ITensor, ::ITensor)\ndag(T::ITensor; kwargs...)\ndirectsum(::Pair{ITensor},::Pair{ITensor},::Pair{ITensor},args...; kws...)\nexp(::ITensor, ::Any, ::Any)\nnullspace(::ITensor, ::Any...)","category":"page"},{"location":"ITensorType.html#Base.:*-Tuple{ITensor, ITensor}","page":"ITensor","title":"Base.:*","text":"A::ITensor * B::ITensor\ncontract(A::ITensor, B::ITensor)\n\nContract ITensors A and B to obtain a new ITensor. This contraction * operator finds all matching indices common to A and B and sums over them, such that the result will have only the unique indices of A and B. To prevent indices from matching, their prime level or tags can be modified such that they no longer compare equal - for more information see the documentation on Index objects.\n\nExamples\n\ni = Index(2,\"index_i\"); j = Index(4,\"index_j\"); k = Index(3,\"index_k\")\n\nA = randomITensor(i,j)\nB = randomITensor(j,k)\nC = A * B # contract over Index j\n\nA = randomITensor(i,i')\nB = randomITensor(i,i'')\nC = A * B # contract over Index i\n\nA = randomITensor(i)\nB = randomITensor(j)\nC = A * B # outer product of A and B, no contraction\n\nA = randomITensor(i,j,k)\nB = randomITensor(k,i,j)\nC = A * B # inner product of A and B, all indices contracted\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.dag-Tuple{ITensor}","page":"ITensor","title":"ITensors.dag","text":"dag(T::ITensor; allow_alias = true)\n\nComplex conjugate the elements of the ITensor T and dagger the indices.\n\nBy default, an alias of the ITensor is returned (i.e. the output ITensor may share data with the input ITensor). If allow_alias = false, an alias is never returned.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.directsum-Tuple{Pair{ITensor}, Pair{ITensor}, Pair{ITensor}, Vararg{Any}}","page":"ITensor","title":"ITensors.directsum","text":"directsum(A::Pair{ITensor}, B::Pair{ITensor}, ...; tags)\n\ndirectsum(output_inds, A::Pair{ITensor}, B::Pair{ITensor}, ...; tags)\n\nGiven a list of pairs of ITensors and indices, perform a partial direct sum of the tensors over the specified indices. Indices that are not specified to be summed must match between the tensors.\n\n(Note: Pair{ITensor} in Julia is short for Pair{ITensor,<:Any} which means any pair T => x where T is an ITensor.)\n\nIf all indices are specified then the operation is equivalent to creating a block diagonal tensor.\n\nReturns the ITensor representing the partial direct sum as well as the new direct summed indices. The tags of the direct summed indices are specified by the keyword arguments.\n\nOptionally, pass the new direct summed indices of the output tensor as the first argument (either a single Index or a collection), which must be proper direct sums of the input indices that are specified to be direct summed.\n\nSee Section 2.3 of https://arxiv.org/abs/1405.7786 for a definition of a partial direct sum of tensors.\n\nExamples\n\nx = Index(2, \"x\")\ni1 = Index(3, \"i1\")\nj1 = Index(4, \"j1\")\ni2 = Index(5, \"i2\")\nj2 = Index(6, \"j2\")\n\nA1 = randomITensor(x, i1)\nA2 = randomITensor(x, i2)\nS, s = directsum(A1 => i1, A2 => i2)\ndim(s) == dim(i1) + dim(i2)\n\ni1i2 = directsum(i1, i2)\nS = directsum(i1i2, A1 => i1, A2 => i2)\nhasind(S, i1i2)\n\nA3 = randomITensor(x, j1)\nS, s = directsum(A1 => i1, A2 => i2, A3 => j1)\ndim(s) == dim(i1) + dim(i2) + dim(j1)\n\nA1 = randomITensor(i1, x, j1)\nA2 = randomITensor(x, j2, i2)\nS, s = directsum(A1 => (i1, j1), A2 => (i2, j2); tags = [\"sum_i\", \"sum_j\"])\nlength(s) == 2\ndim(s[1]) == dim(i1) + dim(i2)\ndim(s[2]) == dim(j1) + dim(j2)\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Base.exp-Tuple{ITensor, Any, Any}","page":"ITensor","title":"Base.exp","text":"exp(A::ITensor, Linds=Rinds', Rinds=inds(A,plev=0); ishermitian = false)\n\nCompute the exponential of the tensor A by treating it as a matrix A_lr with the left index l running over all indices in Linds and r running over all indices in Rinds.\n\nOnly accepts index lists Linds,Rinds such that: (1) length(Linds) + length(Rinds) == length(inds(A)) (2) length(Linds) == length(Rinds) (3) For each pair of indices (Linds[n],Rinds[n]), Linds[n] and Rinds[n] represent the same Hilbert space (the same QN structure in the QN case, or just the same length in the dense case), and appear in A with opposite directions.\n\nWhen ishermitian=true the exponential of Hermitian(A_{lr}) is computed internally.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#LinearAlgebra.nullspace-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"LinearAlgebra.nullspace","text":"nullspace(T::ITensor, left_inds...; tags=\"n\", atol=1E-12, kwargs...)\n\nViewing the ITensor T as a matrix with the provided left_inds viewed as the row space and remaining indices viewed as the right indices or column space, the nullspace function computes the right null space. That is, it will return a tensor N acting on the right indices of T such that T*N is zero. The returned tensor N will also have a new index with the label \"n\" which indexes through the 'vectors' in the null space.\n\nFor example, if T has the indices i,j,k, calling N = nullspace(T,i,k) returns N with index j such that\n\n ___ ___\n i --| | | |\n | T |--j--| N |--n ≈ 0\n k --| | | |\n --- ---\n\nThe index n can be obtained by calling n = uniqueindex(N,T)\n\nNote that the implementation of this function is subject to change in the future, in which case the precise atol value that gives a certain null space size may change in future versions of ITensor.\n\nKeyword arguments:\n\natol::Float64=1E-12 - singular values of T†*T below this value define the null space\ntags::String=\"n\" - choose the tags of the index selecting elements of the null space\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Decompositions","page":"ITensor","title":"Decompositions","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"svd(::ITensor, ::Any...)\neigen(::ITensor, ::Any, ::Any)\nfactorize(::ITensor, ::Any...)","category":"page"},{"location":"ITensorType.html#LinearAlgebra.svd-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"LinearAlgebra.svd","text":"svd(A::ITensor, inds::Index...; )\n\nSingular value decomposition (SVD) of an ITensor A, computed by treating the \"left indices\" provided collectively as a row index, and the remaining \"right indices\" as a column index (matricization of a tensor).\n\nThe first three return arguments are U, S, and V, such that A ≈ U * S * V.\n\nWhether or not the SVD performs a trunction depends on the keyword arguments provided.\n\nIf the left or right set of indices are empty, all input indices are put on V or U respectively. To specify an empty set of left indices, you must explicitly use svd(A, ()) (svd(A) is currently undefined).\n\nExamples\n\nComputing the SVD of an order-three ITensor, such that the indices i and k end up on U and j ends up on V\n\ni = Index(2)\nj = Index(5)\nk = Index(2)\nA = randomITensor(i, j, k)\nU, S, V = svd(A, i, k);\n@show norm(A - U * S * V) <= 10 * eps() * norm(A)\n\nThe following code will truncate the last 2 singular values, since the total number of singular values is 4. The norm of the difference with the original tensor will be the sqrt root of the sum of the squares of the singular values that get truncated.\n\ntrunc, Strunc, Vtrunc = svd(A, i, k; maxdim=2);\n@show norm(A - Utrunc * Strunc * Vtrunc) ≈ sqrt(S[3, 3]^2 + S[4, 4]^2)\n\nAlternatively we can specify that we want to truncate the weights of the singular values up to a certain cutoff, so the total error will be no larger than the cutoff.\n\nUtrunc2, Strunc2, Vtrunc2 = svd(A, i, k; cutoff=1e-10);\n@show norm(A - Utrunc2 * Strunc2 * Vtrunc2) <= 1e-10\n\nKeywords\n\nmaxdim::Int: the maximum number of singular values to keep.\nmindim::Int: the minimum number of singular values to keep.\ncutoff::Float64: set the desired truncation error of the SVD, by default defined as the sum of the squares of the smallest singular values.\nlefttags::String = \"Link,u\": set the tags of the Index shared by U and S.\nrighttags::String = \"Link,v\": set the tags of the Index shared by S and V.\nalg::String = \"divide_and_conquer\". Options:\n\"divide_and_conquer\" - A divide-and-conquer algorithm. LAPACK's gesdd. Fast, but may lead to some innacurate singular values for very ill-conditioned matrices. Also may sometimes fail to converge, leading to errors (in which case \"qr_iteration\" or \"recursive\" can be tried).\n\"qr_iteration\" - Typically slower but more accurate for very ill-conditioned matrices compared to \"divide_and_conquer\". LAPACK's gesvd.\n\"recursive\" - ITensor's custom svd. Very reliable, but may be slow if high precision is needed. To get an svd of a matrix A, an eigendecomposition of A^dagger A is used to compute U and then a qr of A^dagger U is used to compute V. This is performed recursively to compute small singular values.\nuse_absolute_cutoff::Bool = false: set if all probability weights below the cutoff value should be discarded, rather than the sum of discarded weights.\nuse_relative_cutoff::Bool = true: set if the singular values should be normalized for the sake of truncation.\nmin_blockdim::Int = 0: for SVD of block-sparse or QN ITensors, require that the number of singular values kept be greater than or equal to this value when possible\n\nSee also: factorize, eigen\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#LinearAlgebra.eigen-Tuple{ITensor, Any, Any}","page":"ITensor","title":"LinearAlgebra.eigen","text":"eigen(A::ITensor[, Linds, Rinds]; )\n\nEigendecomposition of an ITensor A, computed by treating the \"left indices\" Linds provided collectively as a row index, and remaining \"right indices\" Rinds as a column index (matricization of a tensor).\n\nIf no indices are provided, pairs of primed and unprimed indices are searched for, with Linds taken to be the primed indices and Rinds taken to be the unprimed indices.\n\nThe return arguments are the eigenvalues D and eigenvectors U as tensors, such that A * U ∼ U * D (more precisely they are approximately equal up to proper replacements of indices, see the example for details).\n\nWhether or not eigen performs a trunction depends on the keyword arguments provided. Note that truncation is only well defined for positive semidefinite matrices.\n\nArguments\n\n- `maxdim::Int`: the maximum number of singular values to keep.\n- `mindim::Int`: the minimum number of singular values to keep.\n- `cutoff::Float64`: set the desired truncation error of the eigenvalues,\n by default defined as the sum of the squares of the smallest eigenvalues.\n For now truncation is only well defined for positive semi-definite\n eigenspectra.\n- `ishermitian::Bool = false`: specify if the matrix is Hermitian, in which\n case a specialized diagonalization routine will be used and it is\n guaranteed that real eigenvalues will be returned.\n- `plev::Int = 0`: set the prime level of the Indices of `D`. Default prime\n levels are subject to change.\n- `leftplev::Int = plev`: set the prime level of the Index unique to `D`.\n Default prime levels are subject to change.\n- `rightplev::Int = leftplev+1`: set the prime level of the Index shared\n by `D` and `U`. Default tags are subject to change.\n- `tags::String = \"Link,eigen\"`: set the tags of the Indices of `D`.\n Default tags are subject to change.\n- `lefttags::String = tags`: set the tags of the Index unique to `D`.\n Default tags are subject to change.\n- `righttags::String = tags`: set the tags of the Index shared by `D` and `U`.\n Default tags are subject to change.\n- `use_absolute_cutoff::Bool = false`: set if all probability weights below\n the `cutoff` value should be discarded, rather than the sum of discarded\n weights.\n- `use_relative_cutoff::Bool = true`: set if the singular values should\n be normalized for the sake of truncation.\n\nExamples\n\ni, j, k, l = Index(2, \"i\"), Index(2, \"j\"), Index(2, \"k\"), Index(2, \"l\")\nA = randomITensor(i, j, k, l)\nLinds = (i, k)\nRinds = (j, l)\nD, U = eigen(A, Linds, Rinds)\ndl, dr = uniqueind(D, U), commonind(D, U)\nUl = replaceinds(U, (Rinds..., dr) => (Linds..., dl))\nA * U ≈ Ul * D # true\n\nSee also: svd, factorize\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#LinearAlgebra.factorize-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"LinearAlgebra.factorize","text":"factorize(A::ITensor, Linds::Index...; )\n\nPerform a factorization of A into ITensors L and R such that A ≈ L * R.\n\nArguments\n\northo::String = \"left\": Choose orthogonality properties of the factorization.\n\"left\": the left factor L is an orthogonal basis such that L * dag(prime(L, commonind(L,R))) ≈ I.\n\"right\": the right factor R forms an orthogonal basis.\n\"none\", neither of the factors form an orthogonal basis, and in general are made as symmetrically as possible (depending on the decomposition used).\nwhich_decomp::Union{String, Nothing} = nothing: choose what kind of decomposition is used.\nnothing: choose the decomposition automatically based on the other arguments. For example, when nothing is chosen and ortho = \"left\" or \"right\", and a cutoff is provided, svd or eigen is used depending on the provided cutoff (eigen is only used when the cutoff is greater than 1e-12, since it has a lower precision). When no truncation is requested qr is used for dense ITensors and svd for block-sparse ITensors (in the future qr will be used also for block-sparse ITensors in this case).\n\"svd\": L = U and R = S * V for ortho = \"left\", L = U * S and R = V for ortho = \"right\", and L = U * sqrt.(S) and R = sqrt.(S) * V for ortho = \"none\". To control which svd algorithm is choose, use the svd_alg keyword argument. See the documentation for svd for the supported algorithms, which are the same as those accepted by the alg keyword argument.\n\"eigen\": L = U and R = U^dagger A where U is determined from the eigendecompositon A A^dagger = U D U^dagger for ortho = \"left\" (and vice versa for ortho = \"right\"). \"eigen\" is not supported for ortho = \"none\".\n\"qr\": L=Q and R an upper-triangular matrix when ortho = \"left\", and R = Q and L a lower-triangular matrix when ortho = \"right\" (currently supported for dense ITensors only). In the future, other decompositions like QR (for block-sparse ITensors), polar, cholesky, LU, etc. are expected to be supported.\n\nFor truncation arguments, see: svd\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Memory-operations","page":"ITensor","title":"Memory operations","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"permute(::ITensor, ::Any)\ndense(::ITensor)\ndenseblocks(::ITensor)","category":"page"},{"location":"ITensorType.html#ITensors.permute-Tuple{ITensor, Any}","page":"ITensor","title":"ITensors.permute","text":"permute(T::ITensor, inds...; allow_alias = false)\n\nReturn a new ITensor T with indices permuted according to the input indices inds. The storage of the ITensor is permuted accordingly.\n\nIf called with allow_alias = true, it avoids copying data if possible. Therefore, it may return an alias of the input ITensor (an ITensor that shares the same data), such as if the permutation turns out to be trivial.\n\nBy default, allow_alias = false, and it never returns an alias of the input ITensor.\n\nExamples\n\ni = Index(2, \"index_i\"); j = Index(4, \"index_j\"); k = Index(3, \"index_k\");\nT = randomITensor(i, j, k)\n\npT_1 = permute(T, k, i, j)\npT_2 = permute(T, j, i, k)\n\npT_noalias_1 = permute(T, i, j, k)\npT_noalias_1[1, 1, 1] = 12\nT[1, 1, 1] != pT_noalias_1[1, 1, 1]\n\npT_noalias_2 = permute(T, i, j, k; allow_alias = false)\npT_noalias_2[1, 1, 1] = 12\nT[1, 1, 1] != pT_noalias_1[1, 1, 1]\n\npT_alias = permute(T, i, j, k; allow_alias = true)\npT_alias[1, 1, 1] = 12\nT[1, 1, 1] == pT_alias[1, 1, 1]\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.dense-Tuple{ITensor}","page":"ITensor","title":"NDTensors.dense","text":"dense(T::ITensor)\n\nMake a new ITensor where the storage is the closest Dense storage, avoiding allocating new data if possible. For example, an ITensor with Diag storage will become Dense storage, filled with zeros except for the diagonal values.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.denseblocks-Tuple{ITensor}","page":"ITensor","title":"NDTensors.denseblocks","text":"denseblocks(T::ITensor)\n\nMake a new ITensor where any blocks which have a sparse format, such as diagonal sparsity, are made dense while still preserving the outer block-sparse structure. This method avoids allocating new data if possible.\n\nFor example, an ITensor with DiagBlockSparse storage will have BlockSparse storage afterwards.\n\n\n\n\n\n","category":"method"},{"location":"Sweeps.html#Sweeps","page":"Sweeps","title":"Sweeps","text":"","category":"section"},{"location":"Sweeps.html","page":"Sweeps","title":"Sweeps","text":"Sweeps\nSweeps(nsw::Int, d::AbstractMatrix)","category":"page"},{"location":"Sweeps.html#ITensors.ITensorMPS.Sweeps","page":"Sweeps","title":"ITensors.ITensorMPS.Sweeps","text":"A Sweeps objects holds information about the various parameters controlling a density matrix renormalization group (DMRG) or similar matrix product state (MPS) calculation.\n\nFor a Sweeps object sw the available parameters are:\n\nnsweep(sw) – the number of sweeps to do\nmaxdim(sw,n) – maximum MPS bond dimension for sweep n\nmindim(sw,n) – minimum MPS bond dimension for sweep n\ncutoff(sw,n) – truncation error cutoff for sweep n\nnoise(sw,n) – noise term coefficient for sweep n\n\n\n\n\n\n","category":"type"},{"location":"Sweeps.html#ITensors.ITensorMPS.Sweeps-Tuple{Int64, AbstractMatrix}","page":"Sweeps","title":"ITensors.ITensorMPS.Sweeps","text":"Sweeps(d::AbstractMatrix)\n\nSweeps(nsweep::Int, d::AbstractMatrix)\n\nMake a sweeps object from a matrix of input values. The first row should be strings that define which variables are being set (\"maxdim\", \"cutoff\", \"mindim\", and \"noise\").\n\nIf the number of sweeps are not specified, they are determined from the size of the input matrix.\n\nExamples\n\njulia > Sweeps(\n [\n \"maxdim\" \"mindim\" \"cutoff\" \"noise\"\n 50 10 1e-12 1E-7\n 100 20 1e-12 1E-8\n 200 20 1e-12 1E-10\n 400 20 1e-12 0\n 800 20 1e-12 1E-11\n 800 20 1e-12 0\n ],\n)\nSweeps\n1cutoff = 1.0E-12, maxdim = 50, mindim = 10, noise = 1.0E-07\n2cutoff = 1.0E-12, maxdim = 100, mindim = 20, noise = 1.0E-08\n3cutoff = 1.0E-12, maxdim = 200, mindim = 20, noise = 1.0E-10\n4cutoff = 1.0E-12, maxdim = 400, mindim = 20, noise = 0.0E+00\n5cutoff = 1.0E-12, maxdim = 800, mindim = 20, noise = 1.0E-11\n6cutoff = 1.0E-12, maxdim = 800, mindim = 20, noise = 0.0E+00\n\n\n\n\n\n","category":"method"},{"location":"Sweeps.html#Modifying-Sweeps-Objects","page":"Sweeps","title":"Modifying Sweeps Objects","text":"","category":"section"},{"location":"Sweeps.html","page":"Sweeps","title":"Sweeps","text":"setmaxdim!\nsetcutoff!\nsetnoise!\nsetmindim!","category":"page"},{"location":"Sweeps.html#ITensors.ITensorMPS.setmaxdim!","page":"Sweeps","title":"ITensors.ITensorMPS.setmaxdim!","text":"maxdim!(sw::Sweeps,maxdims::Int...)\n\nSet the maximum MPS bond dimension for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.\n\n\n\n\n\n","category":"function"},{"location":"Sweeps.html#ITensors.ITensorMPS.setcutoff!","page":"Sweeps","title":"ITensors.ITensorMPS.setcutoff!","text":"cutoff!(sw::Sweeps,maxdims::Int...)\n\nSet the MPS truncation error used for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.\n\n\n\n\n\n","category":"function"},{"location":"Sweeps.html#ITensors.ITensorMPS.setnoise!","page":"Sweeps","title":"ITensors.ITensorMPS.setnoise!","text":"noise!(sw::Sweeps,maxdims::Int...)\n\nSet the noise-term coefficient used for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.\n\n\n\n\n\n","category":"function"},{"location":"Sweeps.html#ITensors.ITensorMPS.setmindim!","page":"Sweeps","title":"ITensors.ITensorMPS.setmindim!","text":"mindim!(sw::Sweeps,maxdims::Int...)\n\nSet the minimum MPS bond dimension for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.\n\n\n\n\n\n","category":"function"},{"location":"Sweeps.html#Getting-Sweeps-Object-Data","page":"Sweeps","title":"Getting Sweeps Object Data","text":"","category":"section"},{"location":"Sweeps.html","page":"Sweeps","title":"Sweeps","text":"nsweep(sw::Sweeps)\nmaxdim(sw::Sweeps,n::Int)\ncutoff(sw::Sweeps,n::Int)\nnoise(sw::Sweeps,n::Int)\nmindim(sw::Sweeps,n::Int)","category":"page"},{"location":"Sweeps.html#ITensors.ITensorMPS.nsweep-Tuple{Sweeps}","page":"Sweeps","title":"ITensors.ITensorMPS.nsweep","text":"nsweep(sw::Sweeps)\nlength(sw::Sweeps)\n\nObtain the number of sweeps parameterized by this sweeps object.\n\n\n\n\n\n","category":"method"},{"location":"Sweeps.html#NDTensors.maxdim-Tuple{Sweeps, Int64}","page":"Sweeps","title":"NDTensors.maxdim","text":"maxdim(sw::Sweeps,n::Int)\n\nMaximum MPS bond dimension allowed by the Sweeps object sw during sweep n\n\n\n\n\n\n","category":"method"},{"location":"Sweeps.html#ITensors.ITensorMPS.cutoff-Tuple{Sweeps, Int64}","page":"Sweeps","title":"ITensors.ITensorMPS.cutoff","text":"cutoff(sw::Sweeps,n::Int)\n\nTruncation error cutoff setting of the Sweeps object sw during sweep n\n\n\n\n\n\n","category":"method"},{"location":"Sweeps.html#ITensors.ITensorMPS.noise-Tuple{Sweeps, Int64}","page":"Sweeps","title":"ITensors.ITensorMPS.noise","text":"noise(sw::Sweeps,n::Int)\n\nNoise term coefficient setting of the Sweeps object sw during sweep n\n\n\n\n\n\n","category":"method"},{"location":"Sweeps.html#NDTensors.mindim-Tuple{Sweeps, Int64}","page":"Sweeps","title":"NDTensors.mindim","text":"mindim(sw::Sweeps,n::Int)\n\nMinimum MPS bond dimension allowed by the Sweeps object sw during sweep n\n\n\n\n\n\n","category":"method"},{"location":"DeveloperGuide.html#Developer-Guide","page":"Developer Guide","title":"Developer Guide","text":"","category":"section"},{"location":"DeveloperGuide.html#Keyword-Argument-Best-Practices","page":"Developer Guide","title":"Keyword Argument Best Practices","text":"","category":"section"},{"location":"DeveloperGuide.html","page":"Developer Guide","title":"Developer Guide","text":"Keyword arguments such as f(x,y; a=1, b=2) are a powerful Julia feature, but it is easy to misuse them in library code. Below are the \"best practices\" for using keyword arguments when developing ITensor library code.","category":"page"},{"location":"DeveloperGuide.html","page":"Developer Guide","title":"Developer Guide","text":"A particular challenge how to properly use keyword argument \"forwarding\" where the notation f(; a, b, kwargs...) allows any number of keyword arguments to be passed. If a keyword argument is misspelled, then forwarding keywords with kwargs... will silently allow the misspelling, whereas ideally there would be an error message.","category":"page"},{"location":"DeveloperGuide.html","page":"Developer Guide","title":"Developer Guide","text":"Best practices:","category":"page"},{"location":"DeveloperGuide.html","page":"Developer Guide","title":"Developer Guide","text":"Popping Terminal Keyword Arguments: When passing keyword arguments downward through a stack of function calls, if a certain keyword argument will not be used in any functions further down the stack, then these arguments should be listed explicitly to remove them from the keyword arguments.\nFor example, in a call stack fA -> fB -> fC if a keyword argument such as cutoff is used in the body of fB but not in fC, then use the following pattern:\nfunction fA(...; kwargs...)\n ...\n fB(...; kwargs...)\n ...\nend\n\nfunction fB(...; cutoff, kwargs...) # <- explicitly list cutoff here\n ...\n truncate!(psi; cutoff) # <- fB uses cutoff\n fC(...; kwargs...) # fC does not get passed cutoff\nend\n\nfunction fC(...; maxdim, outputlevel) # fC does not use or need the `cutoff` kwarg\n ...\nend\nLeaf Functions Should Not Take kwargs...: Functions which are the last in the call stack to take any keyword arguments should not take keyword arguments by the kwargs... pattern. They should only take an explicit list of keyword arguments, so as to ensure that an error is thrown if a keyword argument is misspelled or missing (if it has no default value).\nExample: fC above is a leaf function and does not have kwargs... in its signature.\nUse Functions to Set Defaults: Keyword arguments can be made optional by providing default values. To avoid having explicit and possibly inconsistent defaults spread all over the library code, use globally defined functions to provide these defaults.\nFor example:\nfunction sum(A::MPS, B::MPS; cutoff=default_cutoff(), kwargs...)\n...\nend\n\nfunction inner(A::MPS, B::MPS; cutoff=default_cutoff(), kwargs...)\n...\nend\nwhere above the default value for the cutoff keyword is provided by a function default_cutoff() that is defined for the whole library.\nUse Named Tuples to \"Tunnel\" Keywords to Leaf Functions: This is a more advanced pattern. In certain situations, there might be multiple leaf functions depending on the execution pathway of the code or in cases where the leaf function is a \"callback\" passed into the code from the upper-level calling code.\nIn such cases, different leaf function implementations may expect different sets of keyword arguments.\nTo avoid requiring all leaf functions to take all possible keyword arguments (or to use the kwargs... pattern as a workaround, breaking rule #2 above), use the following pattern:\nfunction fA(callback, psi; callback_args, kwargs...)\n ...\n callback(psi; callback_args...)\n ...\nend\n\nmy_callback(psi; a, b) = ... # define custom callback function\n\n# Call fA like this:\nfA(my_callback, psi; callback_args = (; a, b))\n\nExternal (non-ITensor) Functions: Though it requires judgment in each case, if the keyword arguments an external (non-ITensor) function accepts are small in number, not expected to change, and known ahead of time, try to list them explicitly if possible (rather than forwarding with kwargs...). Possible exceptions could be if you want to make use of defaults defined for keyword arguments of an external function.","category":"page"},{"location":"QNTricks.html#Symmetric-(QN-Conserving)-Tensors:-Background-and-Usage","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN Conserving) Tensors: Background and Usage","text":"","category":"section"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"Here is a collection of background material and example codes for understanding how symmetric tensors (tensors with conserved quantum numbers) work in ITensors.jl","category":"page"},{"location":"QNTricks.html#Combiners-and-Symmetric-Tensors","page":"Symmetric (QN conserving) tensors: background and usage","title":"Combiners and Symmetric Tensors","text":"","category":"section"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"In ITensors.jl, combiners are special sparse tensors that represent the action of taking the tensor product of one or more indices. It generalizes the idea of reshaping and permuting. For dense ITensors, a combiner is just the action of permuting and reshaping the data of the tensor. For symmetric tensors (quantum number conserving tensors represented as block sparse tensors), the combiner also fuses symmetry sectors together. They can be used for various purposes. Generally they are used internally in the library, for example in order to reshape a high order ITensor into an order 2 ITensor to perform a matrix decomposition like an SVD or eigendecomposition.","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"For example:","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"using ITensors\n\n# This is a short code showing how a combiner\n# can be used to \"flip\" the direction of an Index\ni = Index([QN(0) => 2, QN(1) => 3], \"i\")\nj = Index([QN(0) => 2, QN(1) => 3], \"j\")\nA = randomITensor(i, dag(j))\nC = combiner(i, dag(j); tags = \"c\", dir = dir(i))\ninds(A)\ninds(A * C)","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"You can see that the combiner reshapes the indices of A into a single Index that contains the tensor product of the two input spaces. The spaces have size QN(-1) => 2 * 3, QN(0) => 2 * 2 + 3 * 3, and QN(0) => 2 * 3 (determined from all of the combinations of combining the sectors of the different indices, where the QNs are added and the block dimensions are multiplied). The ordering of the sectors is determined internally by ITensors.jl.","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"You can also use a combiner on a single Index, which can be helpful for changing the direction of an Index or combining multiple sectors of the same symmetry into a single sector:","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"using ITensors\n\n# This is a short code showing how a combiner\n# can be used to \"flip\" the direction of an Index\ni = Index([QN(0) => 2, QN(1) => 3], \"i\")\nj = dag(Index([QN(0) => 2, QN(1) => 3], \"j\"))\nA = randomITensor(i, j)\nC = combiner(j; tags = \"jflip\", dir = -dir(j))\ninds(A)\ninds(A * C)","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"Unless you are writing very specialized custom code with symmetric tensors, this is generally not needed.","category":"page"},{"location":"QNTricks.html#Block-Sparsity-and-Quantum-Numbers","page":"Symmetric (QN conserving) tensors: background and usage","title":"Block Sparsity and Quantum Numbers","text":"","category":"section"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"In general, not all blocks that are allowed according to the flux will actually exist in the tensor (which helps in many cases for efficiency). Usually this would happen when the tensor is first constructed and not all blocks are explicitly set:","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"using ITensors\n\ni = Index([QN(0) => 1, QN(1) => 1])\nA = ITensor(i', dag(i));\nA[2, 2] = 1.0;\n@show A;\nD, U = eigen(A; ishermitian=true);\n@show D;\n@show U;","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"If we had set A[1, 1] = 0.0 as well, then all of the allowed blocks (according to the flux QN(0) would exist and would be included in the eigendecomposition:","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"using ITensors\n\ni = Index([QN(0) => 1, QN(1) => 1])\nA = ITensor(i', dag(i));\nA[2, 2] = 1.0;\nA[1, 1] = 0.0;\n@show A;\nD, U = eigen(A; ishermitian=true);\n@show D;\n@show U;","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"\"Missing\" blocks can also occur with tensor contractions, since the final blocks of the output tensor are made from combinations of contractions of blocks from the input tensors, and there is no guarantee that all flux-consistent blocks will end up in the result:","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"using ITensors\n\ni = Index([QN(0) => 1, QN(1) => 1])\nj = Index([QN(0) => 1])\nA = ITensor(i, dag(j));\nA[2, 1] = 1.0;\n@show A;\nA2 = prime(A, i) * dag(A);\n@show A2;\nD, U = eigen(A2; ishermitian=true);\n@show D;\n@show U;","category":"page"},{"location":"getting_started/DebugChecks.html#Enabling-Debug-Checks","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"","category":"section"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"ITensor provides some optional checks for common errors, which we call \"debug checks\". These can be enabled with the command:","category":"page"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"ITensors.enable_debug_checks()","category":"page"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"and disabled with the command:","category":"page"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"ITensors.disable_debug_checks()","category":"page"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"We recommend enabling debug checks when you are developing and testing your code, and then disabling them when running in production to get the best performance.","category":"page"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"For example, when debug checks are turned on, ITensor checks that all indices of an ITensor are unique (if they are not unique, it leads to undefined behavior in tensor operations like contraction, addition, and decomposition):","category":"page"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"julia> using ITensors\n\njulia> i = Index(2)\n(dim=2|id=913)\n\njulia> A = randomITensor(i', i)\nITensor ord=2 (dim=2|id=913)' (dim=2|id=913)\nNDTensors.Dense{Float64, Vector{Float64}}\n\njulia> noprime(A)\nITensor ord=2 (dim=2|id=913) (dim=2|id=913)\nNDTensors.Dense{Float64, Vector{Float64}}\n\njulia> ITensors.enable_debug_checks()\nusing_debug_checks (generic function with 1 method)\n\njulia> noprime(A)\nERROR: Trying to create ITensors with collection of indices ((dim=2|id=913), (dim=2|id=913)). Indices must be unique.\nStacktrace:\n [1] error(s::String)\n @ Base ./error.jl:33\n [2] macro expansion\n @ ~/.julia/packages/ITensors/cu9Bo/src/itensor.jl:85 [inlined]\n [3] macro expansion\n @ ~/.julia/packages/ITensors/cu9Bo/src/global_variables.jl:177 [inlined]\n [4] ITensor\n @ ~/.julia/packages/ITensors/cu9Bo/src/itensor.jl:82 [inlined]\n [5] #itensor#123\n @ ~/.julia/packages/ITensors/cu9Bo/src/itensor.jl:123 [inlined]\n [6] itensor(args::NDTensors.DenseTensor{Float64, 2, Tuple{Index{Int64}, Index{Int64}}, NDTensors.Dense{Float64, Vector{Float64}}})\n @ ITensors ~/.julia/packages/ITensors/cu9Bo/src/itensor.jl:123\n [7] noprime(::ITensor; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})\n @ ITensors ~/.julia/packages/ITensors/cu9Bo/src/itensor.jl:1211\n [8] noprime(::ITensor)\n @ ITensors ~/.julia/packages/ITensors/cu9Bo/src/itensor.jl:1211\n [9] top-level scope\n @ REPL[7]:1","category":"page"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"You can track where debug checks are located in the code here, and add your own debug checks to your own code by wrapping your code with the macro ITensors.@debug_check.","category":"page"},{"location":"IndexType.html#Index","page":"Index","title":"Index","text":"","category":"section"},{"location":"IndexType.html#Description","page":"Index","title":"Description","text":"","category":"section"},{"location":"IndexType.html","page":"Index","title":"Index","text":"Index\nITensors.QNIndex","category":"page"},{"location":"IndexType.html#ITensors.Index","page":"Index","title":"ITensors.Index","text":"An Index represents a single tensor index with fixed dimension dim. Copies of an Index compare equal unless their tags are different.\n\nAn Index carries a TagSet, a set of tags which are small strings that specify properties of the Index to help distinguish it from other Indices. There is a special tag which is referred to as the integer tag or prime level which can be incremented or decremented with special priming functions.\n\nInternally, an Index has a fixed id number, which is how the ITensor library knows two indices are copies of a single original Index. Index objects must have the same id, as well as the tags to compare equal.\n\n\n\n\n\n","category":"type"},{"location":"IndexType.html#ITensors.QNIndex","page":"Index","title":"ITensors.QNIndex","text":"A QN Index is an Index with QN block storage instead of just an integer dimension. The QN block storage is a vector of pairs of QNs and block dimensions. The total dimension of a QN Index is the sum of the dimensions of the blocks of the Index.\n\n\n\n\n\n","category":"type"},{"location":"IndexType.html#Constructors","page":"Index","title":"Constructors","text":"","category":"section"},{"location":"IndexType.html","page":"Index","title":"Index","text":"Index(::Int)\nIndex(::Int, ::Union{AbstractString, TagSet})\nIndex(::Pair{QN, Int}...)\nIndex(::Vector{Pair{QN, Int}})\nIndex(::Vector{Pair{QN, Int}}, ::Union{AbstractString, TagSet})","category":"page"},{"location":"IndexType.html#ITensors.Index-Tuple{Int64}","page":"Index","title":"ITensors.Index","text":"Index(dim::Int; tags::Union{AbstractString, TagSet} = \"\",\n plev::Int = 0)\n\nCreate an Index with a unique id, a TagSet given by tags, and a prime level plev.\n\nExamples\n\njulia> i = Index(2; tags=\"l\", plev=1)\n(dim=2|id=818|\"l\")'\n\njulia> dim(i)\n2\n\njulia> plev(i)\n1\n\njulia> tags(i)\n\"l\"\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.Index-Tuple{Int64, Union{ITensors.TagSets.GenericTagSet{BitIntegers.UInt256, 4}, AbstractString}}","page":"Index","title":"ITensors.Index","text":"Index(dim::Integer, tags::Union{AbstractString, TagSet}; plev::Int = 0)\n\nCreate an Index with a unique id and a tagset given by tags.\n\nExamples\n\njulia> i = Index(2, \"l,tag\")\n(dim=2|id=58|\"l,tag\")\n\njulia> dim(i)\n2\n\njulia> plev(i)\n0\n\njulia> tags(i)\n\"l,tag\"\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.Index-Tuple{Vararg{Pair{QN, Int64}}}","page":"Index","title":"ITensors.Index","text":"Index(qnblocks::Pair{QN, Int64}...; dir::Arrow = Out,\n tags = \"\",\n plev::Integer = 0)\n\nConstruct a QN Index from a list of pairs of QN and block dimensions.\n\nExample\n\nIndex(QN(\"Sz\", -1) => 1, QN(\"Sz\", 1) => 1; tags = \"i\")\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.Index-Tuple{Vector{Pair{QN, Int64}}}","page":"Index","title":"ITensors.Index","text":"Index(qnblocks::Vector{Pair{QN, Int64}}; dir::Arrow = Out,\n tags = \"\",\n plev::Integer = 0)\n\nConstruct a QN Index from a Vector of pairs of QN and block dimensions.\n\nNote: in the future, this may enforce that all blocks have the same QNs (which would allow for some optimizations, for example when constructing random QN ITensors).\n\nExample\n\nIndex([QN(\"Sz\", -1) => 1, QN(\"Sz\", 1) => 1]; tags = \"i\")\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.Index-Tuple{Vector{Pair{QN, Int64}}, Union{ITensors.TagSets.GenericTagSet{BitIntegers.UInt256, 4}, AbstractString}}","page":"Index","title":"ITensors.Index","text":"Index(qnblocks::Vector{Pair{QN, Int64}}, tags; dir::Arrow = Out,\n plev::Integer = 0)\n\nConstruct a QN Index from a Vector of pairs of QN and block dimensions.\n\nExample\n\nIndex([QN(\"Sz\", -1) => 1, QN(\"Sz\", 1) => 1], \"i\"; dir = In)\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#Properties","page":"Index","title":"Properties","text":"","category":"section"},{"location":"IndexType.html","page":"Index","title":"Index","text":"id(::Index)\nhasid(::Index, ::ITensors.IDType)\ntags(::Index)\nITensors.set_strict_tags!(::Bool)\nITensors.using_strict_tags()\nhastags(::Index, ::Union{AbstractString,TagSet})\nplev(::Index)\nhasplev(::Index, ::Int)\ndim(::Index)\n==(::Index, ::Index)\ndir(::Index)\nhasqns(::Index)","category":"page"},{"location":"IndexType.html#ITensors.id-Tuple{Index}","page":"Index","title":"ITensors.id","text":"id(i::Index)\n\nObtain the id of an Index, which is a unique 64 digit integer.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.hasid-Tuple{Index, UInt64}","page":"Index","title":"ITensors.hasid","text":"hasid(i::Index, id::ITensors.IDType)\n\nCheck if an Index i has the provided id.\n\nExamples\n\njulia> i = Index(2)\n(dim=2|id=321)\n\njulia> hasid(i, id(i))\ntrue\n\njulia> j = Index(2)\n(dim=2|id=17)\n\njulia> hasid(i, id(j))\nfalse\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.tags-Tuple{Index}","page":"Index","title":"ITensors.tags","text":"tags(i::Index)\n\nObtain the TagSet of an Index.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.TagSets.set_strict_tags!-Tuple{Bool}","page":"Index","title":"ITensors.TagSets.set_strict_tags!","text":"set_strict_tags!(enable::Bool) -> Bool\n\n\nEnable or disable checking for overflow of the number of tags of a TagSet or the number of characters of a tag. If enabled (set to true), an error will be thrown if overflow occurs, otherwise the overflow will be ignored and the extra tags or tag characters will be dropped. This could cause unexpected bugs if tags are being used to distinguish Index objects that have the same ids and prime levels, but that is generally discouraged and should only be used if you know what you are doing.\n\nSee also ITensors.using_strict_tags.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.TagSets.using_strict_tags-Tuple{}","page":"Index","title":"ITensors.TagSets.using_strict_tags","text":"using_strict_tags() -> Bool\n\n\nSee if checking for overflow of the number of tags of a TagSet or the number of characters of a tag is enabled or disabled.\n\nSee also ITensors.set_strict_tags!.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.TagSets.hastags-Tuple{Index, Union{ITensors.TagSets.GenericTagSet{BitIntegers.UInt256, 4}, AbstractString}}","page":"Index","title":"ITensors.TagSets.hastags","text":"hastags(i::Index, ts::Union{AbstractString,TagSet})\n\nCheck if an Index i has the provided tags, which can be a string of comma-separated tags or a TagSet object.\n\nExamples\n\njulia> i = Index(2, \"SpinHalf,Site,n=3\")\n(dim=2|id=861|\"Site,SpinHalf,n=3\")\n\njulia> hastags(i, \"SpinHalf,Site\")\ntrue\n\njulia> hastags(i, \"Link\")\nfalse\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.plev-Tuple{Index}","page":"Index","title":"ITensors.plev","text":"plev(i::Index)\n\nObtain the prime level of an Index.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.hasplev-Tuple{Index, Int64}","page":"Index","title":"ITensors.hasplev","text":"hasplev(i::Index, plev::Int)\n\nCheck if an Index i has the provided prime level.\n\nExamples\n\njulia> i = Index(2; plev=2)\n(dim=2|id=543)''\n\njulia> hasplev(i, 2)\ntrue\n\njulia> hasplev(i, 1)\nfalse\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#NDTensors.dim-Tuple{Index}","page":"Index","title":"NDTensors.dim","text":"dim(i::Index)\n\nObtain the dimension of an Index.\n\nFor a QN Index, this is the sum of the block dimensions.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#Base.:==-Tuple{Index, Index}","page":"Index","title":"Base.:==","text":"==(i1::Index, i1::Index)\n\nCompare indices for equality. First the id's are compared, then the prime levels are compared, and finally the tags are compared.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.dir-Tuple{Index}","page":"Index","title":"ITensors.dir","text":"dir(i::Index)\n\nReturn the direction of an Index (ITensors.In, ITensors.Out, or ITensors.Neither).\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.hasqns-Tuple{Index}","page":"Index","title":"ITensors.hasqns","text":"hasqns(::Index)\n\nChecks of the Index has QNs or not.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#Priming-and-tagging-methods","page":"Index","title":"Priming and tagging methods","text":"","category":"section"},{"location":"IndexType.html","page":"Index","title":"Index","text":"prime(::Index, ::Int)\nadjoint(::Index)\n^(::Index, ::Int)\nsetprime(::Index, ::Int)\nnoprime(::Index)\nsettags(::Index, ::Any)\naddtags(::Index, ::Any)\nremovetags(::Index, ::Any)\nreplacetags(::Index, ::Any, ::Any)","category":"page"},{"location":"IndexType.html#ITensors.prime-Tuple{Index, Int64}","page":"Index","title":"ITensors.prime","text":"prime(i::Index, plinc::Int = 1)\n\nReturn a copy of Index i with its prime level incremented by the amount plinc\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#Base.adjoint-Tuple{Index}","page":"Index","title":"Base.adjoint","text":"adjoint(i::Index)\n\nPrime an Index using the notation i'.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#Base.:^-Tuple{Index, Int64}","page":"Index","title":"Base.:^","text":"^(i::Index, pl::Int)\n\nPrime an Index using the notation i^3.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.setprime-Tuple{Index, Int64}","page":"Index","title":"ITensors.setprime","text":"setprime(i::Index, plev::Int)\n\nReturn a copy of Index i with its prime level set to plev\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.noprime-Tuple{Index}","page":"Index","title":"ITensors.noprime","text":"noprime(i::Index)\n\nReturn a copy of Index i with its prime level set to zero.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.settags-Tuple{Index, Any}","page":"Index","title":"ITensors.settags","text":"settags(i::Index, ts)\n\nReturn a copy of Index i with tags replaced by the ones given The ts argument can be a comma-separated string of tags or a TagSet.\n\nExamples\n\njulia> i = Index(2, \"SpinHalf,Site,n=3\")\n(dim=2|id=543|\"Site,SpinHalf,n=3\")\n\njulia> hastags(i, \"Link\")\nfalse\n\njulia> j = settags(i, \"Link,n=4\")\n(dim=2|id=543|\"Link,n=4\")\n\njulia> hastags(j, \"Link\")\ntrue\n\njulia> hastags(j, \"n=4,Link\")\ntrue\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.TagSets.addtags-Tuple{Index, Any}","page":"Index","title":"ITensors.TagSets.addtags","text":"addtags(i::Index,ts)\n\nReturn a copy of Index i with the specified tags added to the existing ones. The ts argument can be a comma-separated string of tags or a TagSet.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.TagSets.removetags-Tuple{Index, Any}","page":"Index","title":"ITensors.TagSets.removetags","text":"removetags(i::Index, ts)\n\nReturn a copy of Index i with the specified tags removed. The ts argument can be a comma-separated string of tags or a TagSet.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.TagSets.replacetags-Tuple{Index, Any, Any}","page":"Index","title":"ITensors.TagSets.replacetags","text":"replacetags(i::Index, tsold, tsnew)\n\nreplacetags(i::Index, tsold => tsnew)\n\nIf the tag set of i contains the tags specified by tsold, replaces these with the tags specified by tsnew, preserving any other tags. The arguments tsold and tsnew can be comma-separated strings of tags, or TagSet objects.\n\nExamples\n\njulia> i = Index(2; tags=\"l,x\", plev=1)\n(dim=2|id=83|\"l,x\")'\n\njulia> replacetags(i, \"l\", \"m\")\n(dim=2|id=83|\"m,x\")'\n\njulia> replacetags(i, \"l\" => \"m\")\n(dim=2|id=83|\"m,x\")'\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#Methods","page":"Index","title":"Methods","text":"","category":"section"},{"location":"IndexType.html","page":"Index","title":"Index","text":"sim(::Index)\ndag(::Index)\nremoveqns(::Index)","category":"page"},{"location":"IndexType.html#NDTensors.sim-Tuple{Index}","page":"Index","title":"NDTensors.sim","text":"sim(i::Index; tags = tags(i), plev = plev(i), dir = dir(i))\n\nProduces an Index with the same properties (dimension or QN structure) but with a new id.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.dag-Tuple{Index}","page":"Index","title":"ITensors.dag","text":"dag(i::Index)\n\nCopy an index i and reverse its direction.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.removeqns-Tuple{Index}","page":"Index","title":"ITensors.removeqns","text":"removeqns(::Index)\n\nRemoves the QNs from the Index, if it has any.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#Iterating","page":"Index","title":"Iterating","text":"","category":"section"},{"location":"IndexType.html","page":"Index","title":"Index","text":"eachval(::Index)\neachindval(::Index)","category":"page"},{"location":"IndexType.html#ITensors.eachval-Tuple{Index}","page":"Index","title":"ITensors.eachval","text":"eachval(i::Index)\n\nCreate an iterator whose values range over the dimension of the provided Index.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.eachindval-Tuple{Index}","page":"Index","title":"ITensors.eachindval","text":"eachindval(i::Index)\n\nCreate an iterator whose values are Pairs of the form i=>n with n from 1:dim(i). This iterator is useful for accessing elements of an ITensor in a loop without needing to know the ordering of the indices. See also eachindval(is::Index...).\n\n\n\n\n\n","category":"method"},{"location":"UpgradeGuide_0.1_to_0.2.html#Upgrade-guide","page":"Upgrading from 0.1 to 0.2","title":"Upgrade guide","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html#Upgrading-from-ITensors.jl-0.1-to-0.2","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from ITensors.jl 0.1 to 0.2","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"The main breaking changes in ITensor.jl v0.2 involve changes to the ITensor, IndexSet, and IndexVal types. Most user code should be fine, but see below for more details.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"In addition, we have moved development of NDTensors.jl into ITensors.jl to simplify the development process until NDTensors is more stable and can be a standalone package. Again, see below for more details.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"For a more comprehensive list of changes, see the commit history on Github.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"If you have issues upgrading, please reach out by raising an issue on Github or asking a question on the ITensor support forum.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Also make sure to run your code with julia --depwarn=yes to see warnings about function names and interfaces that have been deprecated and will be removed in v0.3 of ITensors.jl (these are not listed here).","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#Major-design-changes:-changes-to-the-ITensor,-IndexSet,-and-IndexVal-types","page":"Upgrading from 0.1 to 0.2","title":"Major design changes: changes to the ITensor, IndexSet, and IndexVal types","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html#Changes-to-the-ITensor-type","page":"Upgrading from 0.1 to 0.2","title":"Changes to the ITensor type","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html#Removal-of-tensor-order-type-parameter","page":"Upgrading from 0.1 to 0.2","title":"Removal of tensor order type parameter","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"The tensor order type paramater has been removed from the ITensor type, so you can no longer write ITensor{3} to specify an order 3 ITensor (PR #591). Code that uses the ITensor order type parameter will now lead to the following error:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> i = Index(2)\n(dim=2|id=588)\n\njulia> ITensor{2}(i', i)\nERROR: TypeError: in Type{...} expression, expected UnionAll, got Type{ITensor}\nStacktrace:\n [1] top-level scope\n @ REPL[27]:1","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Simply remove the type parameter:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> ITensor(i', i)\nITensor ord=2 (dim=2|id=913)' (dim=2|id=913)\nITensors.NDTensors.EmptyStorage{ITensors.NDTensors.EmptyNumber, ITensors.NDTensors.Dense{ITensors.NDTensors.EmptyNumber, Vector{ITensors.NDTensors.EmptyNumber}}}","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Pro tip: from the command line, you can replace all examples like that with:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"find . -type f -iname \"*.jl\" -exec sed -i 's/ITensor{.*}/ITensor/g' \"{}\" +","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Of course, make sure to back up your code before running this!","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Additionally, a common code pattern may be using the type parameter for dispatch:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"using ITensors\n\nfunction mynorm(A::ITensor{N}) where {N}\n return norm(A)^N\nend\n\nfunction mynorm(A::ITensor{1})\n return norm(A)\nend\n\nfunction mynorm(A::ITensor{2})\n return norm(A)^2\nend","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Instead, you can use an if-statement:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"function mynormN(A::ITensor)\n return norm(A)^order(A)\nend\n\nfunction mynorm1(A::ITensor)\n return norm(A)\nend\n\nfunction mynorm2(A::ITensor)\n return norm(A)^2\nend\n\nfunction mynorm(A::ITensor)\n return if order(A) == 1\n mynorm1(A)\n elseif order(A) == 2\n mynorm2(A)\n else\n return mynormN(A)\n end\nend","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Alternatively, you can use the Order type to dispatch on the ITensor order as follows:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"function mynorm(::Order{N}, A::ITensor) where {N}\n return norm(A)^N\nend\n\nfunction mynorm(::Order{1}, A::ITensor)\n return norm(A)\nend\n\nfunction mynorm(::Order{2}, A::ITensor)\n return norm(A)^2\nend\n\nfunction mynorm(A::ITensor)\n return mynorm(Order(A), A)\nend","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Order(A::ITensor) returns the order of the ITensor (like order(A::ITensor)), however as a type that can be dispatched on. Note that it is not type stable, so there will be a small runtime overhead for doing this.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#Change-to-storage-type-of-Index-collection-in-ITensor","page":"Upgrading from 0.1 to 0.2","title":"Change to storage type of Index collection in ITensor","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"ITensors now store a Tuple of Index instead of an IndexSet (PR #626). Therefore, calling inds on an ITensor will now just return a Tuple:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> i = Index(2)\n(dim=2|id=770)\n\njulia> j = Index(3)\n(dim=3|id=272)\n\njulia> A = randomITensor(i, j)\nITensor ord=2 (dim=2|id=770) (dim=3|id=272)\nITensors.NDTensors.Dense{Float64, Vector{Float64}}\n\njulia> inds(A)\n((dim=2|id=770), (dim=3|id=272))","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"while before it returned an IndexSet (in fact, the IndexSet type has been removed, see below for details). In general, this should not affect user code, since a Tuple of Index should have all of the same functions defined for it that IndexSet did. If you find this is not the case, please raise an issue on Github or on the ITensor support forum.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#ITensor-type-now-directly-wraps-a-Tensor","page":"Upgrading from 0.1 to 0.2","title":"ITensor type now directly wraps a Tensor","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"The ITensor type no longer has separate field inds and store, just a single field tensor (PR #626). In general you should not be accessing the fields directly, instead you should be using the functions inds(A::ITensor) and storage(A::ITensor), so this should not affect most code. However, in case you have code like:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"i = Index(2)\nA = randomITensor(i)\nA.inds","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"this will error in v0.2 with:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> A.inds\nERROR: type ITensor has no field inds\nStacktrace:\n [1] getproperty(x::ITensor, f::Symbol)\n @ Base ./Base.jl:33\n [2] top-level scope\n @ REPL[43]:1","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"and you should change it to:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"inds(A)","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#Changes-to-the-ITensor-constructors","page":"Upgrading from 0.1 to 0.2","title":"Changes to the ITensor constructors","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html#Plain-ITensor-constructors-now-return-ITensors-with-EmptyStorage-storage","page":"Upgrading from 0.1 to 0.2","title":"Plain ITensor constructors now return ITensors with EmptyStorage storage","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"ITensor constructors from collections of Index, such as ITensor(i, j, k), now return an ITensor with EmptyStorage (previously called Empty) storage instead of Dense or BlockSparse storage filled with 0 values. Most operations should still work that worked previously, but please contact us if there are issues (PR #641).","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"For example:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> i = Index(2)\n(dim=2|id=346)\n\njulia> A = ITensor(i', dag(i))\nITensor ord=2 (dim=2|id=346)' (dim=2|id=346)\nITensors.NDTensors.EmptyStorage{ITensors.NDTensors.EmptyNumber, ITensors.NDTensors.Dense{ITensors.NDTensors.EmptyNumber, Vector{ITensors.NDTensors.EmptyNumber}}}\n\njulia> A' * A\nITensor ord=2 (dim=2|id=346)'' (dim=2|id=346)\nITensors.NDTensors.EmptyStorage{ITensors.NDTensors.EmptyNumber, ITensors.NDTensors.Dense{ITensors.NDTensors.EmptyNumber, Vector{ITensors.NDTensors.EmptyNumber}}}","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"so now contracting two EmptyStorage ITensors returns another EmptyStorage ITensor. You can allocate the storage by setting elements of the ITensor:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> A[i' => 1, i => 1] = 0.0\n0.0\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=346)'\nDim 2: (dim=2|id=346)\nITensors.NDTensors.Dense{Float64, Vector{Float64}}\n 2×2\n 0.0 0.0\n 0.0 0.0","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Additionally, it will take on the element type of the first value set:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> A = ITensor(i', dag(i))\nITensor ord=2 (dim=2|id=346)' (dim=2|id=346)\nITensors.NDTensors.EmptyStorage{ITensors.NDTensors.EmptyNumber, ITensors.NDTensors.Dense{ITensors.NDTensors.EmptyNumber, Vector{ITensors.NDTensors.EmptyNumber}}}\n\njulia> A[i' => 1, i => 1] = 1.0 + 0.0im\n1.0 + 0.0im\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=346)'\nDim 2: (dim=2|id=346)\nITensors.NDTensors.Dense{ComplexF64, Vector{ComplexF64}}\n 2×2\n 1.0 + 0.0im 0.0 + 0.0im\n 0.0 + 0.0im 0.0 + 0.0im","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"If you have issues upgrading, please let us know.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#Slight-change-to-automatic-conversion-of-element-type-when-constructing-ITensor-from-Array","page":"Upgrading from 0.1 to 0.2","title":"Slight change to automatic conversion of element type when constructing ITensor from Array","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"ITensor constructors from Array now only convert to floating point for Array{Int} and Array{Complex{Int}}. That same conversion is added for QN ITensor constructors to be consistent with non-QN versions (PR #620). Previously it tried to convert arrays of any element type to the closest floating point type with Julia's float function. This should not affect most user code.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#Changes-to-the-IndexSet-type","page":"Upgrading from 0.1 to 0.2","title":"Changes to the IndexSet type","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"The IndexSet type has been removed in favor of Julia's Tuple and Vector types (PR #626). ITensors now contain a Tuple of Index, while set operations like commoninds that used to return IndexSet now return a Vector of Index:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> i = Index(2)\n(dim=2|id=320)\n\njulia> A = randomITensor(i', i)\nITensor ord=2 (dim=2|id=320)' (dim=2|id=320)\nITensors.NDTensors.Dense{Float64, Vector{Float64}}\n\njulia> inds(A) # Previously returned IndexSet, now returns Tuple\n((dim=2|id=320)', (dim=2|id=320))\n\njulia> commoninds(A', A) # Previously returned IndexSet, now returns Vector\n1-element Vector{Index{Int64}}:\n (dim=2|id=320)'","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"To help with upgrading code, IndexSet{IndexT} has been redefined as a type alias for Vector{IndexT<:Index} (which is subject to change to some other collection of indices, and likely will be removed in ITensors v0.3). Therefore it no longer has a type parameter for the number of indices, similar to the change to the ITensor type. If you were using the plain IndexSet type, code should generally still work properly. However, if you were using the type parameters of IndexSet, such as:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"function myorder2(is::IndexSet{N}) where {N}\n return N^2\nend","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"then you will need to remove the type parameter and rewrite your code generically to accept Tuple or Vector, such as:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"function myorder2(is)\n return length(is)^2\nend","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"In general you should be able to just remove usages of IndexSet in your code, and can just use Tuple or Vector of Index instead, such as change is = IndexSet(i, j, k) to is = (i, j, k) or is = [i, j, k]. Priming, tagging, and set operations now work generically on those types. If you see issues with upgrading your code, please let us know.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#Changes-to-the-IndexVal-type","page":"Upgrading from 0.1 to 0.2","title":"Changes to the IndexVal type","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Similar to the removal of IndexSet, we have also removed the IndexVal type (PR #665). Now, all use cases of IndexVal can be replaced by using Julia's Pair type, for example instead of:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"i = Index(2)\nIndexVal(i, 2)","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"use:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"i = Index(2)\ni => 2\n# Or:\nPair(i, 2)","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Note that we have made IndexVal{IndexT} an alias for Pair{IndexT,Int}, so code using IndexVal such as IndexVal(i, 2) should generally still work. However, we encourage users to change from IndexVal(i, 2) to i => 2.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#NDTensors.jl-package-now-being-developed-internally-within-ITensors.jl","page":"Upgrading from 0.1 to 0.2","title":"NDTensors.jl package now being developed internally within ITensors.jl","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"The NDTensors module has been moved into the ITensors package, so ITensors no longer depends on the standalone NDTensors package. This should only effect users who were using both NDTensors and ITensors seperately. If you want to use the latest NDTensors library, you should do using ITensors.NDTensors instead of using NDTensors, and will need to install ITensors with using Pkg; Pkg.add(\"ITensors\") in order to use the latest versions of NDTensors. Note the current NDTensors.jl package will still exist, but for now developmentof NDTensors will occur within ITensors.jl (PR #650).","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#Miscellaneous-breaking-changes","page":"Upgrading from 0.1 to 0.2","title":"Miscellaneous breaking changes","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html#state-function-renamed-val,-state-given-a-new-more-general-definition","page":"Upgrading from 0.1 to 0.2","title":"state function renamed val, state given a new more general definition","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Rename the state functions currently defined for various site types to val for mapping a string name for an index to an index value (used in ITensor indexing and MPS construction). state functions now return single-index ITensors representing various single-site states (PR #664). So now to get an Index value from a string, you use:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"N = 10\ns = siteinds(\"S=1/2\", N)\nval(s[1], \"Up\") == 1\nval(s[1], \"Dn\") == 2","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"state now returns an ITensor corresponding to the state with that value as the only nonzero element:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> @show state(s[1], \"Up\");\nstate(s[1], \"Up\") = ITensor ord=1\nDim 1: (dim=2|id=597|\"S=1/2,Site,n=1\")\nITensors.NDTensors.Dense{Float64, Vector{Float64}}\n 2-element\n 1.0\n 0.0\n\njulia> @show state(s[1], \"Dn\");\nstate(s[1], \"Dn\") = ITensor ord=1\nDim 1: (dim=2|id=597|\"S=1/2,Site,n=1\")\nITensors.NDTensors.Dense{Float64, Vector{Float64}}\n 2-element\n 0.0\n 1.0","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"which allows for more general states to be defined, such as:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> @show state(s[1], \"X+\");\nstate(s[1], \"X+\") = ITensor ord=1\nDim 1: (dim=2|id=597|\"S=1/2,Site,n=1\")\nITensors.NDTensors.Dense{Float64, Vector{Float64}}\n 2-element\n 0.7071067811865475\n 0.7071067811865475\n\njulia> @show state(s[1], \"X-\");\nstate(s[1], \"X-\") = ITensor ord=1\nDim 1: (dim=2|id=597|\"S=1/2,Site,n=1\")\nITensors.NDTensors.Dense{Float64, Vector{Float64}}\n 2-element\n 0.7071067811865475\n -0.7071067811865475","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"which will be used for making more general MPS product states.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"This should not affect end users in general, besides ones who had customized the previous state function, such as with overloads like:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"ITensors.state(::SiteType\"My_S=1/2\", ::StateName\"Up\") = 1\nITensors.state(::SiteType\"My_S=1/2\", ::StateName\"Dn\") = 2","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"which should be changed now to:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"ITensors.val(::SiteType\"My_S=1/2\", ::StateName\"Up\") = 1\nITensors.val(::SiteType\"My_S=1/2\", ::StateName\"Dn\") = 2","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#\"Qubit\"-site-type-QN-convention-change","page":"Upgrading from 0.1 to 0.2","title":"\"Qubit\" site type QN convention change","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"The QN convention of the \"Qubit\" site type is changed to track the total number of 1 bits instead of the net number of 1 bits vs 0 bits (i.e. change the QN from +1/-1 to 0/1) (PR #676).","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> s = siteinds(\"Qubit\", 4; conserve_number=true)\n4-element Vector{Index{Vector{Pair{QN, Int64}}}}:\n (dim=2|id=925|\"Qubit,Site,n=1\") \n 1: QN(\"Number\",0) => 1\n 2: QN(\"Number\",1) => 1\n (dim=2|id=799|\"Qubit,Site,n=2\") \n 1: QN(\"Number\",0) => 1\n 2: QN(\"Number\",1) => 1\n (dim=2|id=8|\"Qubit,Site,n=3\") \n 1: QN(\"Number\",0) => 1\n 2: QN(\"Number\",1) => 1\n (dim=2|id=385|\"Qubit,Site,n=4\") \n 1: QN(\"Number\",0) => 1\n 2: QN(\"Number\",1) => 1","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Before it was +1/-1 like \"S=1/2\":","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> s = siteinds(\"S=1/2\", 4; conserve_sz=true)\n4-element Vector{Index{Vector{Pair{QN, Int64}}}}:\n (dim=2|id=364|\"S=1/2,Site,n=1\") \n 1: QN(\"Sz\",1) => 1\n 2: QN(\"Sz\",-1) => 1\n (dim=2|id=823|\"S=1/2,Site,n=2\") \n 1: QN(\"Sz\",1) => 1\n 2: QN(\"Sz\",-1) => 1\n (dim=2|id=295|\"S=1/2,Site,n=3\") \n 1: QN(\"Sz\",1) => 1\n 2: QN(\"Sz\",-1) => 1\n (dim=2|id=810|\"S=1/2,Site,n=4\") \n 1: QN(\"Sz\",1) => 1\n 2: QN(\"Sz\",-1) => 1","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"This shouldn't affect end users in general. The new convention is a bit more intuitive since the quantum number can be thought of as counting the total number of 1 bits in the state, though the conventions can be mapped to each other with a constant.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#maxlinkdim-for-MPS/MPO-with-no-indices","page":"Upgrading from 0.1 to 0.2","title":"maxlinkdim for MPS/MPO with no indices","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"maxlinkdim(::MPS/MPO) returns a minimum of 1 (previously it returned 0 for MPS/MPO without and link indices) (PR #663).","category":"page"},{"location":"index.html#ITensors.jl","page":"Introduction","title":"ITensors.jl","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"ITensor is a library for rapidly creating correct and efficient tensor network algorithms.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Documentation Citation Build Status\n(Image: docs) (Image: SciPost) (Image: arXiv) (Image: Tests) (Image: codecov)","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Version Download Statistics Style Guide License\n(Image: version) (Image: ITensor Downloads) (Image: Code Style: Blue) (Image: license)","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"The source code for ITensor can be found on Github.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Additional documentation can be found on the ITensor website itensor.org.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"An ITensor is a tensor whose interface is independent of its memory layout. ITensor indices are objects which carry extra information and which 'recognize' each other (compare equal to each other).","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"The ITensor library also includes composable and extensible algorithms for optimizing and transforming tensor networks, such as matrix product state and matrix product operators, such as the DMRG algorithm.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Development of ITensor is supported by the Flatiron Institute, a division of the Simons Foundation.","category":"page"},{"location":"index.html#News","page":"Introduction","title":"News","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"May 2, 2024: ITensors.jl v0.5 has been released. This version removes PackageCompiler.jl as a dependency and moves the package compilation functionality into a package extension. In order to use the ITensors.compile() function going forward, you need to install the PackageCompiler.jl package with using Pkg: Pkg; Pkg.add(\"PackageCompiler\") and put using PackageCompiler together with using ITensors in your code.\nApril 16, 2024: ITensors.jl v0.4 has been released. This version removes HDF5.jl as a dependency and moves the HDF5 read and write functions for ITensor, MPS, MPO, and other associated types into a package extension. To enable ITensor HDF5 features, install the HDF5.jl package with using Pkg: Pkg; Pkg.add(\"HDF5\") and put using HDF5 together with using ITensors in your code. Other recent changes include support for multiple GPU backends using package extensions.\nMarch 25, 2022: ITensors.jl v0.3 has been released. The main breaking change is that we no longer support versions of Julia below 1.6. Julia 1.6 is the long term support version of Julia (LTS), which means that going forward versions below Julia 1.6 won't be as well supported with bug fixes and improvements. Additionally, Julia 1.6 introduced many improvements including syntax improvements that we would like to start using with ITensors.jl, which becomes challenging if we try to support Julia versions below 1.6. See here and here for some nice summaries of the Julia 1.6 release.\nJun 09, 2021: ITensors.jl v0.2 has been released, with a few breaking changes as well as a variety of bug fixes","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"and new features. Take a look at the upgrade guide for help upgrading your code.","category":"page"},{"location":"index.html#Installation","page":"Introduction","title":"Installation","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"The ITensors package can be installed with the Julia package manager. From the Julia REPL, type ] to enter the Pkg REPL mode and run:","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"~ julia","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"julia> ]\n\npkg> add ITensors","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Or, equivalently, via the Pkg API:","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"julia> import Pkg; Pkg.add(\"ITensors\")","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Please note that right now, ITensors.jl requires that you use Julia v1.3 or later (since ITensors.jl relies on a feature that was introduced in Julia v1.3).","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"We recommend using ITensors.jl with Intel MKL in order to get the best possible performance. If you have not done so already, you can replace your current BLAS and LAPACK implementation with MKL by using the MKL.jl package. Please follow the instructions here.","category":"page"},{"location":"index.html#Documentation","page":"Introduction","title":"Documentation","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"LATEST – documentation of the latest version.","category":"page"},{"location":"index.html#Citation","page":"Introduction","title":"Citation","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"If you use ITensor in your work, please cite the ITensor Paper:","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"@article{ITensor,\n\ttitle={{The ITensor Software Library for Tensor Network Calculations}},\n\tauthor={Matthew Fishman and Steven R. White and E. Miles Stoudenmire},\n\tjournal={SciPost Phys. Codebases},\n\tpages={4},\n\tyear={2022},\n\tpublisher={SciPost},\n\tdoi={10.21468/SciPostPhysCodeb.4},\n\turl={https://scipost.org/10.21468/SciPostPhysCodeb.4},\n}","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"and associated \"Codebase Release\" for the version you have used. The current one is","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"@article{ITensor-r0.3,\n\ttitle={{Codebase release 0.3 for ITensor}},\n\tauthor={Matthew Fishman and Steven R. White and E. Miles Stoudenmire},\n\tjournal={SciPost Phys. Codebases},\n\tpages={4-r0.3},\n\tyear={2022},\n\tpublisher={SciPost},\n\tdoi={10.21468/SciPostPhysCodeb.4-r0.3},\n\turl={https://scipost.org/10.21468/SciPostPhysCodeb.4-r0.3},\n}","category":"page"},{"location":"index.html#ITensor-Code-Samples","page":"Introduction","title":"ITensor Code Samples","text":"","category":"section"},{"location":"index.html#Basic-Overview","page":"Introduction","title":"Basic Overview","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"ITensor construction, setting of elements, contraction, and addition. Before constructing an ITensor, one constructs Index objects representing tensor indices.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"using ITensors\nlet\n i = Index(3)\n j = Index(5)\n k = Index(2)\n l = Index(7)\n\n A = ITensor(i,j,k)\n B = ITensor(j,l)\n\n # Set elements of A\n A[i=>1,j=>1,k=>1] = 11.1\n A[i=>2,j=>1,k=>2] = -21.2\n A[k=>1,i=>3,j=>1] = 31.1 # can provide Index values in any order\n # ...\n\n # Contract over shared index j\n C = A * B\n\n @show hasinds(C,i,k,l) # = true\n\n D = randomITensor(k,j,i) # ITensor with random elements\n\n # Add two ITensors\n # must have same set of indices\n # but can be in any order\n R = A + D\n\n nothing\nend\n\n# output\n\nhasinds(C, i, k, l) = true","category":"page"},{"location":"index.html#Singular-Value-Decomposition-(SVD)-of-a-Matrix","page":"Introduction","title":"Singular Value Decomposition (SVD) of a Matrix","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"In this example, we create a random 10x20 matrix and compute its SVD. The resulting factors can be simply multiplied back together using the ITensor * operation, which automatically recognizes the matching indices between U and S, and between S and V and contracts (sums over) them.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"using ITensors\nlet\n i = Index(10) # index of dimension 10\n j = Index(20) # index of dimension 20\n M = randomITensor(i,j) # random matrix, indices i,j\n U,S,V = svd(M,i) # compute SVD with i as row index\n @show M ≈ U*S*V # = true\n\n nothing\nend\n\n# output\n\nM ≈ U * S * V = true","category":"page"},{"location":"index.html#Singular-Value-Decomposition-(SVD)-of-a-Tensor","page":"Introduction","title":"Singular Value Decomposition (SVD) of a Tensor","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"In this example, we create a random 4x4x4x4 tensor and compute its SVD, temporarily treating the indices i and k together as the \"row\" index and j and l as the \"column\" index for the purposes of the SVD. The resulting factors can be simply multiplied back together using the ITensor * operation, which automatically recognizes the matching indices between U and S, and between S and V and contracts (sums over) them.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"(Image: )","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"using ITensors\nlet\n i = Index(4,\"i\")\n j = Index(4,\"j\")\n k = Index(4,\"k\")\n l = Index(4,\"l\")\n T = randomITensor(i,j,k,l)\n U,S,V = svd(T,i,k) # compute SVD with (i,k) as row indices (indices of U)\n @show hasinds(U,i,k) # = true\n @show hasinds(V,j,l) # = true\n @show T ≈ U*S*V # = true\n\n nothing\nend\n\n# output\n\nhasinds(U, i, k) = true\nhasinds(V, j, l) = true\nT ≈ U * S * V = true","category":"page"},{"location":"index.html#Tensor-Indices:-Tags-and-Prime-Levels","page":"Introduction","title":"Tensor Indices: Tags and Prime Levels","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Before making an ITensor, you have to define its indices. Tensor Index objects carry extra information beyond just their dimension.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"All Index objects carry a permanent, immutable id number which is determined when it is constructed, and allow it to be matched (compare equal) with copies of itself.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Additionally, an Index can have up to four tag strings, and an integer primelevel. If two Index objects have different tags or different prime levels, they do not compare equal even if they have the same id.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Tags are also useful for identifying Index objects when printing tensors, and for performing certain Index manipulations (e.g. priming indices having certain sets of tags).","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"using ITensors\nlet\n i = Index(3) # Index of dimension 3\n @show dim(i) # = 3\n @show id(i) # = 0x5d28aa559dd13001 or similar\n\n ci = copy(i)\n @show ci == i # = true\n\n j = Index(5,\"j\") # Index with a tag \"j\"\n\n @show j == i # = false\n\n s = Index(2,\"n=1,Site\") # Index with two tags,\n # \"Site\" and \"n=1\"\n @show hastags(s,\"Site\") # = true\n @show hastags(s,\"n=1\") # = true\n\n i1 = prime(i) # i1 has a \"prime level\" of 1\n # but otherwise same properties as i\n @show i1 == i # = false, prime levels do not match\n\n nothing\nend\n\n# output\n\ndim(i) = 3\nid(i) = 0x5d28aa559dd13001\nci == i = true\nj == i = false\nhastags(s, \"Site\") = true\nhastags(s, \"n=1\") = true\ni1 == i = false","category":"page"},{"location":"index.html#DMRG-Calculation","page":"Introduction","title":"DMRG Calculation","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"DMRG is an iterative algorithm for finding the dominant eigenvector of an exponentially large, Hermitian matrix. It originates in physics with the purpose of finding eigenvectors of Hamiltonian (energy) matrices which model the behavior of quantum systems.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"using ITensors\nlet\n # Create 100 spin-one indices\n N = 100\n sites = siteinds(\"S=1\",N)\n\n # Input operator terms which define\n # a Hamiltonian matrix, and convert\n # these terms to an MPO tensor network\n # (here we make the 1D Heisenberg model)\n os = OpSum()\n for j=1:N-1\n os += \"Sz\",j,\"Sz\",j+1\n os += 0.5,\"S+\",j,\"S-\",j+1\n os += 0.5,\"S-\",j,\"S+\",j+1\n end\n H = MPO(os,sites)\n\n # Create an initial random matrix product state\n psi0 = randomMPS(sites)\n\n # Plan to do 5 passes or 'sweeps' of DMRG,\n # setting maximum MPS internal dimensions\n # for each sweep and maximum truncation cutoff\n # used when adapting internal dimensions:\n nsweeps = 5\n maxdim = [10,20,100,100,200]\n cutoff = 1E-10\n\n # Run the DMRG algorithm, returning energy\n # (dominant eigenvalue) and optimized MPS\n energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff)\n println(\"Final energy = $energy\")\n\n nothing\nend\n\n# output\n\nAfter sweep 1 energy=-137.954199761732 maxlinkdim=9 maxerr=2.43E-16 time=9.356\nAfter sweep 2 energy=-138.935058943878 maxlinkdim=20 maxerr=4.97E-06 time=0.671\nAfter sweep 3 energy=-138.940080155429 maxlinkdim=92 maxerr=1.00E-10 time=4.522\nAfter sweep 4 energy=-138.940086009318 maxlinkdim=100 maxerr=1.05E-10 time=11.644\nAfter sweep 5 energy=-138.940086058840 maxlinkdim=96 maxerr=1.00E-10 time=12.771\nFinal energy = -138.94008605883985","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"You can find more examples of running dmrg and related algorithms here.","category":"page"},{"location":"tutorials/QN_DMRG.html#Quantum-Number-Conserving-DMRG","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"","category":"section"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"An important technique in DMRG calculations of quantum Hamiltonians is the conservation of quantum numbers. Examples of these are the total number of particles of a model of fermions, or the total of all S^z components of a system of spins. Not only can conserving quantum numbers make DMRG calculations run more quickly and use less memory, but it can be important for simulating physical systems with conservation laws and for obtaining ground states in different symmetry sectors. Note that ITensor currently only supports Abelian quantum numbers.","category":"page"},{"location":"tutorials/QN_DMRG.html#Necessary-Changes","page":"Quantum Number Conserving DMRG","title":"Necessary Changes","text":"","category":"section"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Setting up a quantum-number conserving DMRG calculation in ITensor requires only very small changes to a DMRG code. The main changes are:","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"using tensor indices (Index objects) which carry quantum number (QN) information to build your Hamiltonian and initial state\ninitializing your MPS to have well-defined total quantum numbers","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Importantly, the total QN of your state throughout the calculation will remain the same as the initial state passed to DMRG. The total QN of your state is not set separately, but determined implicitly from the initial QN of the state when it is first constructed.","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Of course, your Hamiltonian should conserve all of the QN's that you would like to use. If it doesn't, you will get an error when you try to construct it out of the QN-enabled tensor indices.","category":"page"},{"location":"tutorials/QN_DMRG.html#Making-the-Changes","page":"Quantum Number Conserving DMRG","title":"Making the Changes","text":"","category":"section"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Let's see how to make these two changes to the DMRG Tutorial code from the previous section. At the end, we will put together these changes for a complete, working code.","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Change 1: QN Site Indices","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"To make change (1), we will change the line","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"sites = siteinds(\"S=1\",N)","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"by setting the conserve_qns keyword argument to true:","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"sites = siteinds(\"S=1\",N; conserve_qns=true)","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Setting conserve_qns=true tells the siteinds function to conserve every possible quantum number associated to the site type (which is \"S=1\" in this example). For S=1 spins, this will turn on total-S^z conservation. (For other site types that conserve multiple QNs, there are specific keyword arguments available to track just a subset of conservable QNs.) We can check this by printing out some of the site indices, and seeing that the subspaces of each Index are labeled by QN values:","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"@show sites[1]\n@show sites[2]","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Sample output:","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":" sites[1] = (dim=3|id=794|\"S=1,Site,n=1\") \n 1: QN(\"Sz\",2) => 1\n 2: QN(\"Sz\",0) => 1\n 3: QN(\"Sz\",-2) => 1\n sites[2] = (dim=3|id=806|\"S=1,Site,n=2\") \n 1: QN(\"Sz\",2) => 1\n 2: QN(\"Sz\",0) => 1\n 3: QN(\"Sz\",-2) => 1","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"In the sample output above, note that in addition to the dimension of these indices being 3, each of the three settings of the Index have a unique QN associated to them. The number after the QN on each line is the dimension of that subspace, which is 1 for each subspace of the Index objects above. Note also that \"Sz\" quantum numbers in ITensor are measured in units of 12, so QN(\"Sz\",2) corresponds to S^z=1 in conventional physics units.","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Change 2: Initial State","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"To make change (2), instead of constructing the initial MPS psi0 to be an arbitrary, random MPS, we will make it a specific state with a well-defined total S^z. So we will replace the line","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"psi0 = randomMPS(sites,10)","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"by the lines","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"state = [isodd(n) ? \"Up\" : \"Dn\" for n=1:N]\npsi0 = MPS(sites,state)","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"The first line of the new code above makes an array of strings which alternate between \"Up\" and \"Dn\" on odd and even numbered sites. These names \"Up\" and \"Dn\" are special values associated to the \"S=1\" site type which indicate up and down spin values. The second line takes the array of site Index objects sites and the array of strings state and returns an MPS which is a product state (classical, unentangled state) with each site's state given by the strings in the state array. In this example, psi0 will be a Neel state with alternating up and down spins, so it will have a total S^z of zero. We could check this by computing the quantum-number flux of psi0","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"@show flux(psi0)\n# Output: flux(psi0) = QN(\"Sz\",0)","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"info: Setting Other Total QN Values\nThe above example shows the case of setting a total \"Sz\" quantum number of zero, since the initial state alternates between \"Up\" and \"Dn\" on every site with an even number of sites.To obtain other total QN values, just set the initial state to be one which has the total QN you want. To be concrete let's take the example of a system with N=10 sites of S=1 spins.For example if you want a total \"Sz\" of +20 (= QN(\"Sz\",20)) in ITensor units, or S^z=10 in physical units, for a system with 10 sites, use the initial state:state = [\"Up\" for n=1:N]\npsi0 = MPS(sites,state)Or to initialize this 10-site system to have a total \"Sz\" of +16 in ITensor units (S^z=8 in physical units):state = [\"Dn\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\"]\npsi0 = MPS(sites,state)would work (as would any state with one \"Dn\" and nine \"Up\"'s in any order). Or you could initialize to a total \"Sz\" of +18 in ITensor units (S^z=9 in physical units) asstate = [\"Z0\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\"]\npsi0 = MPS(sites,state)where \"Z0\" refers to the S^z=0 state of a spin-one spin.Finally, the same kind of logic as above applies to other physical site types, whether \"S=1/2\", \"Electron\", etc.","category":"page"},{"location":"tutorials/QN_DMRG.html#Putting-it-All-Together","page":"Quantum Number Conserving DMRG","title":"Putting it All Together","text":"","category":"section"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Let's take the DMRG Tutorial code from the previous section and make the changes discussed above, to turn it into a code which conserves the total S^z quantum number throughout the DMRG calculation. The resulting code is:","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"using ITensors\nlet\n N = 100\n sites = siteinds(\"S=1\",N;conserve_qns=true)\n\n os = OpSum()\n for j=1:N-1\n os += \"Sz\",j,\"Sz\",j+1\n os += 1/2,\"S+\",j,\"S-\",j+1\n os += 1/2,\"S-\",j,\"S+\",j+1\n end\n H = MPO(os,sites)\n\n state = [isodd(n) ? \"Up\" : \"Dn\" for n=1:N]\n psi0 = MPS(sites,state)\n @show flux(psi0)\n\n nsweeps = 5\n maxdim = [10,20,100,100,200]\n cutoff = [1E-10]\n\n energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff)\n\n return\nend","category":"page"},{"location":"faq/DMRG.html#Density-Matrix-Renormalization-Group-(DMRG)-Frequently-Asked-Questions","page":"DMRG FAQs","title":"Density Matrix Renormalization Group (DMRG) Frequently Asked Questions","text":"","category":"section"},{"location":"faq/DMRG.html#Ensuring-a-DMRG-calculation-is-converged","page":"DMRG FAQs","title":"Ensuring a DMRG calculation is converged","text":"","category":"section"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"While DMRG calculations can be extremely quick to converge in the best cases, convergence can be slower for cases such as gapless systems or quasi-two-dimensional systems. So it becomes important to know if a DMRG calculation is converged i.e. has been run long enough with enough resources (large enough MPS bond dimension).","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Unfortunately there is no automatic or bulletproof check for DMRG convergence. However, there are a number of reliable heuristics you can use to check convergence. We list some of these with the most fundamental and important ones first:","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Run your DMRG calculation on a smaller system and compare with another method, such as an exact diagonalization. If the agreement is good, then gradually try larger systems and see if the physical properties are roughly consistent and similar (i.e. the density profile has similar features).\nMake sure to check a wide range of properties - not just the energy. See if these look plausible by plotting and visually inspecting them. For example: if your system has left-right reflection symmetry, does the density or magnetization also have this symmetry? If the ground state of your system is expected to have a total S^z of zero, does your ground state have this property?\nMake sure to run your DMRG calculation for different numbers of sweeps to see if the results change. For example, if you run DMRG for 5 sweeps but are unsure of convergence, try running it for 10 sweeps: is the energy the same or has it significantly decreased? If 10 sweeps made a difference, try 20 sweeps.\nTry setting the eigsolve_krylovdim keyword argument to a higher value (the default is 3). This can be particularily helpful when the Hamiltonian is close to a critical point. This may make slowly-converging calculations converge in fewer sweeps, but setting it too high can make each sweep run slowly.\nInspect the the DMRG output. The ITensor DMRG code reports the maximum bond or link dimension and maximum truncation error after each sweep. (The maximums here mean over each DMRG substep making up one sweep.) Is the maximum dimension or \"maxlinkdim\" reported by the DMRG output quickly reaching and saturating the maxdim value you set for each sweep? Is the maximum truncation error \"maxerr\" consistently reaching large values, larger than 1E-5? Then it you may need to raise the maxdim parameter for your later sweeps, so that DMRG is allowed to use a larger bond dimension and thus reach a better accuracy.\nCompute the energy variance of an MPS to check whether it is an eigenstate. To do this in ITensor, you can use the following code where H is your Hamiltonian MPO and psi is the wavefunction you want to check:\nH2 = inner(H,psi,H,psi)\nE = inner(psi',H,psi)\nvar = H2-E^2\n@show var\nHere var is the quantity langle H^2 rangle - langle H rangle^2. The closer var is to zero, the more precisely psi is an eigenstate of H. Note that this check does not ensure that psi is the ground state, but only one of the eigenstates.","category":"page"},{"location":"faq/DMRG.html#Preventing-DMRG-from-getting-stuck-in-a-local-minimum","page":"DMRG FAQs","title":"Preventing DMRG from getting stuck in a local minimum","text":"","category":"section"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"While DMRG has very robust convergence properties when the initial MPS is close to the global minimum, if it is far from the global minumum then there is no guarantee that DMRG will be able to find the true ground state. This problem is exacerbated for quantum number conserving DMRG where the search space is more constrained.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Thus it is very important to perform a number of checks to ensure that the result you get from DMRG is actually converged. To learn about these checks, see the previous question.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"When DMRG is failing to converge, here are some of the steps you can take to improve things:","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"The most important and useful technique is to turn on the noise term feature of DMRG. To do this, just set the noise parameter of each sweep to a small, non-zero value, making this value very small (1E-11, say) or zero by the last sweep. (Experiment with different values on small systems to see which noise magnitudes help.) Here is an example of defining DMRG accuracy or sweep parameters with a non-zero noise set for the first three sweeps:\nnsweeps = 10\nmaxdim = [100, 200, 400, 800, 1600]\ncutoff = [1E-6]\nnoise = [1E-6, 1E-7, 1E-8, 0.0]\n...\nenergy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff, noise)\nTry using a initial MPS with properties close to the ground state you are looking for. For example, the ground state of a system of electrons typically has a density which is spread out over the whole system. So if your initial state has all of the electrons bunched up on the left-hand side only, it can take DMRG a very long time to converge.\nTry using a random MPS with a modestly large bond dimension. ITensor offers a function called randomMPS which can be used to make random MPS in both the quantum number (QN) conserving and non-QN conserving cases. Because random MPS have properties which are \"typical\" of most ground states, they can be good initial states for DMRG.\nTry DMRG on a closely related Hamiltonian for which convergence is easier to obtain (be creative here: it could be your Hamiltonian with interactions turned off, or with interactions only within, but not between, small local patches). Take the output of this first calculation and use it as input for DMRG with the full Hamiltonian.\nIn stubborn cases, try other methods for finding the ground state which are slower, but have a better chance of succeeding. A key example is imaginary time evolution, which always reaches the ground state if (a) performed accurately on (b) a state which is not orthogonal to the ground state. After doing some amount of imaginary time evolution, use the resulting MPS as an initial state for DMRG obtain a higher-accuracy solution.","category":"page"},{"location":"faq/DMRG.html#How-to-do-periodic-boundary-condition-DMRG","page":"DMRG FAQs","title":"How to do periodic boundary condition DMRG","text":"","category":"section"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"The short answer to how to do fully periodic boundary condition DMRG in ITensor is that you simply input a periodic Hamiltonian into our OpSum system and make the MPO form of your Hamiltonian in the usual way. For example, for a chain of N sites with nearest-neighbor interactions, you include a term that connects site 1 to site N. For a one-dimensional Ising model chain Hamiltonian this would look like:","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"sites = siteinds(\"S=1/2\",N)\n\nhterms = OpSum()\nfor j=1:(N-1)\n hterms += \"Sz\",j,\"Sz\",j+1\nend\nhterms += \"Sz\",1,\"Sz\",N # term 'wrapping' around the ring\n\nH = MPO(hterms,sites)","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"For two-dimensional DMRG calculations, where the most common approach is to use periodic boundary conditions in the y-direction only, and not in the x-direction, you do a similar step in making your OpSum input to ITensor DMRG: you include terms wrapping around the periodic cylinder in the y direction but not in the x direction.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"However, fully periodic boundary conditions are only recommended for small systems when absolutely needed, and in general are not recommended. For a longer discussion of alternatives to using fully periodic boundaries, see the next section below.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"The reason fully periodic boundary conditions (periodic in x in 1D, and periodic in both x and y in 2D) are not recommended in general is that the DMRG algorithm, as we are defining it here, optimizes an open-boundary MPS. So if you input a periodic-boundary Hamiltonian, there is a kind of \"mismatch\" that happens where you can still get the correct answer, but it requires much more resources (a larger bond dimension and more sweeps) to get good accuracy. There has been some research into \"truly\" periodic DMRG, [Pippan] that is DMRG that optimizes an MPS with a ring-like topology, but it is not widely used, is still an open area of algorithm development, and is not currently available in ITensor.","category":"page"},{"location":"faq/DMRG.html#What-boundary-conditions-should-I-choose:-open,-periodic,-or-infinite?","page":"DMRG FAQs","title":"What boundary conditions should I choose: open, periodic, or infinite?","text":"","category":"section"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"One of the weaknesses of the density matrix renormalization group (DMRG), and its time-dependent or finite-temperature extensions, is that it works poorly with periodic boundary conditions. This stems from the fact that conventional DMRG optimizes over open-boundary matrix product state (MPS) wavefunctions whether or not the Hamiltonian includes periodic interactions.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"But this begs the question, when are periodic boundary conditions (PBC) really needed? DMRG offers some compelling alternatives to PBC:","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Use open boundary conditions (OBC). Though this introduces edge effects, the number of states needed to reach a given accuracy is significantly lower than with PBC (see next section below). For gapped systems DMRG scales linearly with system size, meaning often one can study systems with many hundreds or even thousands of sites. Last but not least, open boundaries are often more natural. For studying systems which spontaneously break symmetry, adding \"pinning\" fields on the edge is often a very nice way to tip the balance toward a certain symmetry broken state while leaving the bulk unmodified.\nUse smooth boundary conditions. The basic idea is to use OBC but send the Hamiltonian parameters smoothly to zero at the boundary so that the system can not \"feel\" the boundary. For certain systems this can significantly reduce edge effects.[Smooth1][Smooth2][Smooth3]","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Smooth1]: Smooth boundary conditions for quantum lattice systems, M. Vekic and Steven R. White, Phys. Rev. Lett. 71, 4283 (1993) cond-mat/9310053","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Smooth2]: Hubbard model with smooth boundary conditions, M. Vekic and Steven R. White, Phys. Rev. B 53, 14552 (1996) cond-mat/9601009","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Smooth3]: Grand canonical finite-size numerical approaches: A route to measuring bulk properties in an applied field, Chisa Hotta and Naokazu Shibata, Phys. Rev. B 86, 041108 (2012)","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Use \"infinite boundary conditions\", that is, use infinite DMRG in the form of an algorithm like iDMRG or VUMPS. This has a cost that can be even less than with OBC yet is completely free of finite-size effects.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"However, there are a handful of cases where PBC remains preferable despite the extra overhead. A few such cases are:","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Benchmarking DMRG against another code that uses PBC, such as a Monte Carlo or exact diagonalization code.\nExtracting the central charge of a critical one-dimensional system described by a CFT. In practice, using PBC can give an accurate central charge even for quite small systems by fitting the subsystem entanglement entropy to the CFT scaling form.\nChecking for the presence or absence of topological effects. These could be edge effects (the Haldane phase has a four-fold ground state degeneracy with OBC, but not with PBC), or could be related to some global topological sector that is ill-defined with PBC (e.g. periodic vs. antiperiodic boundary conditions for the transverse field Ising model).","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"(Note that in the remaining discussion, by PBC I mean fully periodic boundary conditions in all directions. For the case of DMRG applied to quasi-two-dimensional systems, it remains a good practice to use periodic boundaries in the shorter direction, while still using open (or infinite) boundaries in the longer direction along the DMRG/MPS path.)","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Below I discuss more about the problems with using PBC, as well as some misconceptions about when PBC seems necessary even though there are better alternatives.","category":"page"},{"location":"faq/DMRG.html#Drawbacks-of-Periodic-Boundary-Conditions","page":"DMRG FAQs","title":"Drawbacks of Periodic Boundary Conditions","text":"","category":"section"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Periodic boundary conditions are straightforward to implement in conventional DMRG. The simplest approach is to include a \"long bond\" directly connecting site 1 to site N in the Hamiltonian. However this naive approach has a major drawback: if open-boundary DMRG achieves a given accuracy when keeping m states (bond dimension of size m), then to reach the same accuracy with PBC one must keep closer to m^2 states! The reason is that now every bond of the MPS not only carries local entanglement as with OBC, but also the entanglement between the first and last sites. (There is an alternative DMRG algorithm[Pippan] for periodic systems which may have better scaling than the above approach but has not been widely applied and tested, as far as I am aware, especially for 2D or critical systems .)","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Pippan]: Efficient matrix-product state method for periodic boundary conditions, P. Pippan, Steven R. White, and H.G. Evertz, Phys. Rev. B 81, 081103","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"The change in scaling from m to m^2 is a severe problem. For example, many gapped one-dimensional systems only require about m=100 to reach good accuracy (truncation errors of less than 1E-9 or so). To reach the same accuracy with naive PBC would then require using 10,000 states, which can easily fill the RAM of a typical desktop computer for a large enough system, not to mention the extra time needed to work with larger matrices.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"But poor scaling is not the only drawback of PBC. Systems that exhibit spontaneous symmetry breaking are simple to work with under OBC, where one has the additional freedom of applying edge pinning terms to drive the bulk into a specific symmetry sector. Using edge pinning reduces the bulk entanglement and makes measuring order parameters straightforward. Similarly one can use infinite DMRG to directly observe symmetry breaking effects.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"But under PBC, order parameters remain equal to zero and can only be accessed through correlation functions. Though using correlation functions is often presented as the \"standard\" or \"correct\" approach, such reasoning pre-supposes that PBC is the best choice. Recent work in the quantum Monte Carlo community demonstrates that open boundaries with pinning fields can actually be a superior approach.[Assaad]","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Assaad]: Pinning the Order: The Nature of Quantum Criticality in the Hubbard Model on Honeycomb Lattice, Fakher F. Assaad and Igor F. Herbut, Phys. Rev. X 3, 031010","category":"page"},{"location":"faq/DMRG.html#Cases-Where-Periodic-BC-Seems-Necessary,-But-Open/Infinite-BC-Can-be-Better","page":"DMRG FAQs","title":"Cases Where Periodic BC Seems Necessary, But Open/Infinite BC Can be Better","text":"","category":"section"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Below are some cases where periodic boundary conditions seem to be necessary at a first glance. But in many of these cases, not only can open or infinite boundaries be just as successful, they can even be the better choice.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Measuring asymptotic properties of correlation functions: much of our understanding of gapless one-dimensional systems comes from field-theoretic approaches which make specific predictions about asymptotic decays of various correlators. To test these predictions numerically, one must work with large, translationally invariant systems with minimal edge effects. Using fully periodic boundary conditions satisfies these criteria. However, a superior choice is to use infinite DMRG, which combines the much better scaling of open-boundary DMRG with the ability to measure correlators at arbitrarily long distances by repeating the unit cell of the MPS wavefunction. Although truncating to a finite number of states imposes an effective correlation length on the system, this correlation length can reach many thousands of sites for quite moderate MPS bond dimensions. Karrasch and Moore took advantage of this fact to convincingly check the predictions of Luttinger liquid theory for one-dimensional systems of gapless fermions.[Karrasch]","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Karrasch]: Luttinger liquid physics from the infinite-system density matrix renormalization group, C. Karrasch and J.E. Moore, Phys. Rev. B 86, 155156","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Studying two-dimensional topological order: a hallmark of intrinsic topological order is the presence of a robust ground state degeneracy when the system is put on a torus. Also many topological phases have gapless edge states which can cause problems for numerical calculations. Thus one might think that fully periodic BC are the best choice for studying topological phases. However, topological phases have the same ground-state degeneracy on an infinite cylinder as they do on a torus.[Zhang]. Cincio and Vidal exploited this fact to use infinite DMRG to study a variety of topological phases [Cincio]. One part of their calculation did actually require obtaining ground states on a torus, but they accomplished this by taking a finite segment of an infinite MPS and connecting its ends. This approach does not give the true ground state of the torus but was sufficient for their calculation and was arguably closer to the true two-dimensional physics.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Zhang]: Quasiparticle statistics and braiding from ground-state entanglement, Yi Zhang, Tarun Grover, Ari Turner, Masaki Oshkawa, and Ashvin Vishwanath, Phys. Rev. B 85, 235151","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Cincio]: Characterizing Topological Order by Studying the Ground States on an Infinite Cylinder, L. Cincio and G. Vidal, Phys. Rev. Lett. 110, 067208","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Obtaining bulk gaps: DMRG has the ability to \"target\" low-lying excited states or to obtain such states by constraining them to be orthogonal to the ground state. However, with OBC, localized excitations can get stuck to the edges and not reveal the true bulk gap behavior. Thus one may conclude that PBC is necessary. But using open or infinite boundaries remains the better choice because they allow much higher accuracy.\nTo deal with the presence of edges in OBC, one can use \"restricted sweeping\". Here one sweeps across the full system to obtain the ground state. Then, to obtain the first excited state one only sweeps through the full system to obtain the ground state. Then, to obtain the first excited state one only sweeps through the near the edges. This traps the particle in a \"soft box\" which still lets its wavefunction mix with the basis that describes the ground state outside the restricted sweeping region.\nWithin infinite DMRG, boundary effects are rigorously absent if the calculation has converged. To compute bulk gaps one again uses a type of restricted sweeping known in the literature as \"infinite boundary conditions\". For more see the work by Phien, Vidal, and McCulloch.[Phien]","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Phien]: Infinite boundary conditions for matrix product state calculations, Ho N. Phien, G. Vidal, and Ian P. McCulloch Phys. Rev. B 86, 245107","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"In conclusion, consider carefully whether you really need to use periodic boundary conditions, as they impose a steep computational cost within DMRG. Periodic BC can actually be worse for the very types of measurements where they are often presented as the best or \"standard\" choice. Many of the issues periodic boundaries circumvent can be avoided more elegantly by using infinite DMRG, or when that is not applicable, by using open boundary conditions with sufficient care.","category":"page"},{"location":"DMRG.html#DMRG","page":"DMRG","title":"DMRG","text":"","category":"section"},{"location":"DMRG.html","page":"DMRG","title":"DMRG","text":"dmrg","category":"page"},{"location":"DMRG.html#ITensors.ITensorMPS.dmrg","page":"DMRG","title":"ITensors.ITensorMPS.dmrg","text":"dmrg(H::MPO, psi0::MPS; kwargs...)\ndmrg(H::MPO, psi0::MPS, sweeps::Sweeps; kwargs...)\n\nUse the density matrix renormalization group (DMRG) algorithm to optimize a matrix product state (MPS) such that it is the eigenvector of lowest eigenvalue of a Hermitian matrix H, represented as a matrix product operator (MPO).\n\ndmrg(Hs::Vector{MPO}, psi0::MPS; kwargs...)\ndmrg(Hs::Vector{MPO}, psi0::MPS, sweeps::Sweeps; kwargs...)\n\nUse the density matrix renormalization group (DMRG) algorithm to optimize a matrix product state (MPS) such that it is the eigenvector of lowest eigenvalue of a Hermitian matrix H. This version of dmrg accepts a representation of H as a Vector of MPOs, Hs = [H1, H2, H3, ...] such that H is defined as H = H1 + H2 + H3 + ... Note that this sum of MPOs is not actually computed; rather the set of MPOs [H1,H2,H3,..] is efficiently looped over at each step of the DMRG algorithm when optimizing the MPS.\n\ndmrg(H::MPO, Ms::Vector{MPS}, psi0::MPS; weight=1.0, kwargs...)\ndmrg(H::MPO, Ms::Vector{MPS}, psi0::MPS, sweeps::Sweeps; weight=1.0, kwargs...)\n\nUse the density matrix renormalization group (DMRG) algorithm to optimize a matrix product state (MPS) such that it is the eigenvector of lowest eigenvalue of a Hermitian matrix H, subject to the constraint that the MPS is orthogonal to each of the MPS provided in the Vector Ms. The orthogonality constraint is approximately enforced by adding to H terms of the form w|M1>isodd(n) ... is an\n # on-the-fly function mapping integers to strings)\n sites = siteinds(n->isodd(n) ? \"S=1/2\" : \"S=1\",N)\n\n # Couplings between spin-half and\n # spin-one sites:\n Jho = 1.0 # half-one coupling\n Jhh = 0.5 # half-half coupling\n Joo = 0.5 # one-one coupling\n\n os = OpSum()\n for j=1:N-1\n os += 0.5*Jho,\"S+\",j,\"S-\",j+1\n os += 0.5*Jho,\"S-\",j,\"S+\",j+1\n os += Jho,\"Sz\",j,\"Sz\",j+1\n end\n for j=1:2:N-2\n os += 0.5*Jhh,\"S+\",j,\"S-\",j+2\n os += 0.5*Jhh,\"S-\",j,\"S+\",j+2\n os += Jhh,\"Sz\",j,\"Sz\",j+2\n end\n for j=2:2:N-2\n os += 0.5*Joo,\"S+\",j,\"S-\",j+2\n os += 0.5*Joo,\"S-\",j,\"S+\",j+2\n os += Joo,\"Sz\",j,\"Sz\",j+2\n end\n H = MPO(os,sites)\n\n nsweeps = 10\n maxdim = [10,10,20,40,80,100,140,180,200]\n cutoff = [1E-8]\n\n psi0 = randomMPS(sites,4)\n\n energy,psi = dmrg(H,psi0; nsweeps, maxdim, cutoff)\n\n return\nend","category":"page"},{"location":"examples/DMRG.html#Use-a-Sum-of-MPOs-in-DMRG","page":"DMRG Examples","title":"Use a Sum of MPOs in DMRG","text":"","category":"section"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"One version of the ITensor dmrg function accepts an array of MPOs [H1,H2,H3] (or any number of MPOs you want). This version of DMRG will find the ground state of H1+H2+H3. Internally it does not actually sum these MPOs, but loops over them during each step of the \"eigensolver\" at the core of the DMRG algorithm, so it is usually more efficient than if the MPOs had been summed together into a single MPO.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"To use this version of DMRG, say you have MPOs H1, H2, and H3. Then call DMRG like this:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"energy,psi = dmrg([H1,H2,H3],psi0; nsweeps, maxdim, cutoff)","category":"page"},{"location":"examples/DMRG.html#Make-a-2D-Hamiltonian-for-DMRG","page":"DMRG Examples","title":"Make a 2D Hamiltonian for DMRG","text":"","category":"section"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"You can use the OpSum system to make 2D Hamiltonians much in the same way you make 1D Hamiltonians: by looping over all of the bonds and adding the interactions on these bonds to the OpSum.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"To help with the logic of 2D lattices, ITensor pre-defines some helper functions which return an array of bonds. Each bond object has an \"s1\" field and an \"s2\" field which are the integers numbering the two sites the bond connects. (You can view the source for these functions at this link.)","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"The two provided functions currently are square_lattice and triangular_lattice. It is not hard to write your own similar lattice functions as all they have to do is define an array of ITensors.LatticeBond structs or even a custom struct type you wish to define. We welcome any user contributions of other lattices that ITensor does not currently offer.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Each lattice function takes an optional named argument \"yperiodic\" which lets you request that the lattice should have periodic boundary conditions around the y direction, making the geometry a cylinder.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Full example code:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"using ITensors\n\nlet\n Ny = 6\n Nx = 12\n\n N = Nx*Ny\n\n sites = siteinds(\"S=1/2\", N;\n conserve_qns = true)\n\n # Obtain an array of LatticeBond structs\n # which define nearest-neighbor site pairs\n # on the 2D square lattice (wrapped on a cylinder)\n lattice = square_lattice(Nx, Ny; yperiodic = false)\n\n # Define the Heisenberg spin Hamiltonian on this lattice\n os = OpSum()\n for b in lattice\n os .+= 0.5, \"S+\", b.s1, \"S-\", b.s2\n os .+= 0.5, \"S-\", b.s1, \"S+\", b.s2\n os .+= \"Sz\", b.s1, \"Sz\", b.s2\n end\n H = MPO(os,sites)\n\n state = [isodd(n) ? \"Up\" : \"Dn\" for n=1:N]\n # Initialize wavefunction to a random MPS\n # of bond-dimension 10 with same quantum\n # numbers as `state`\n psi0 = randomMPS(sites,state,20)\n\n nsweeps = 10\n maxdim = [20,60,100,100,200,400,800]\n cutoff = [1E-8]\n\n energy,psi = dmrg(H,psi0; nsweeps, maxdim, cutoff)\n\n return\nend","category":"page"},{"location":"examples/DMRG.html#Compute-excited-states-with-DMRG","page":"DMRG Examples","title":"Compute excited states with DMRG","text":"","category":"section"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"ITensor DMRG accepts additional MPS wavefunctions as a optional, extra argument. These additional 'penalty states' are provided as an array of MPS just after the Hamiltonian, like this:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"energy,psi3 = dmrg(H,[psi0,psi1,psi2],psi3_init; nsweeps, maxdim, cutoff)","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Here the penalty states are [psi0,psi1,psi2]. When these are provided, the DMRG code minimizes the energy of the current MPS while also reducing its overlap (inner product) with the previously provided MPS. If these overlaps become sufficiently small, then the computed MPS is an excited state. So by finding the ground state, then providing it to DMRG as a \"penalty state\" or previous state one can compute the first excited state. Then providing both of these, one can get the second excited state, etc.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"A keyword argument called weight can also be provided to the dmrg function when penalizing overlaps to previous states. The weight parameter is multiplied by the overlap with the previous states, so sets the size of the penalty. It should be chosen at least as large as the (estimated) gap between the ground and first excited states. Otherwise the optimal value of the weight parameter is not so obvious, and it is best to try various weights during initial test calculations.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Note that when the system has conserved quantum numbers, a superior way to find excited states can be to find ground states of quantum number (or symmetry) sectors other than the one containing the absolute ground state. In that context, the penalty method used below is a way to find higher excited states within the same quantum number sector.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Full Example code:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"using ITensors\n\nlet\n N = 20\n\n sites = siteinds(\"S=1/2\",N)\n\n h = 4.0\n\n weight = 20*h # use a large weight\n # since gap is expected to be large\n\n\n #\n # Use the OpSum feature to create the\n # transverse field Ising model\n #\n # Factors of 4 and 2 are to rescale\n # spin operators into Pauli matrices\n #\n os = OpSum()\n for j=1:N-1\n os += -4,\"Sz\",j,\"Sz\",j+1\n end\n for j=1:N\n os += -2*h,\"Sx\",j;\n end\n H = MPO(os,sites)\n\n\n #\n # Make sure to do lots of sweeps\n # when finding excited states\n #\n nsweeps = 30\n maxdim = [10,10,10,20,20,40,80,100,200,200]\n cutoff = [1E-8]\n noise = [1E-6]\n\n #\n # Compute the ground state psi0\n #\n psi0_init = randomMPS(sites,linkdims=2)\n energy0,psi0 = dmrg(H,psi0_init; nsweeps, maxdim, cutoff, noise)\n\n println()\n\n #\n # Compute the first excited state psi1\n #\n psi1_init = randomMPS(sites,linkdims=2)\n energy1,psi1 = dmrg(H,[psi0],psi1_init; nsweeps, maxdim, cutoff, noise, weight)\n\n # Check psi1 is orthogonal to psi0\n @show inner(psi1,psi0)\n\n\n #\n # The expected gap of the transverse field Ising\n # model is given by Eg = 2*|h-1|\n #\n # (The DMRG gap will have finite-size corrections)\n #\n println(\"DMRG energy gap = \",energy1-energy0);\n println(\"Theoretical gap = \",2*abs(h-1));\n\n println()\n\n #\n # Compute the second excited state psi2\n #\n psi2_init = randomMPS(sites,linkdims=2)\n energy2,psi2 = dmrg(H,[psi0,psi1],psi2_init; nsweeps, maxdim, cutoff, noise, weight)\n\n # Check psi2 is orthogonal to psi0 and psi1\n @show inner(psi2,psi0)\n @show inner(psi2,psi1)\n\n return\nend","category":"page"},{"location":"examples/DMRG.html#Printing-the-Entanglement-Entropy-at-Each-Step","page":"DMRG Examples","title":"Printing the Entanglement Entropy at Each Step","text":"","category":"section"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"To obtain the entanglement entropy of an MPS at each step during a DMRG calculation, you can use the Observer system to make a custom observer object that prints out this information.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"First we define our custom observer type, EntanglementObserver, and overload the measure! function for it:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"mutable struct EntanglementObserver <: AbstractObserver\nend\n\nfunction ITensors.measure!(o::EntanglementObserver; bond, psi, half_sweep, kwargs...)\n wf_center, other = half_sweep==1 ? (psi[bond+1],psi[bond]) : (psi[bond],psi[bond+1])\n U,S,V = svd(wf_center, uniqueinds(wf_center,other))\n SvN = 0.0\n for n=1:dim(S, 1)\n p = S[n,n]^2\n SvN -= p * log(p)\n end\n println(\" Entanglement across bond $bond = $SvN\")\nend","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"The measure! function grabs certain helpful keywords passed to it by DMRG, such as what bond DMRG has just finished optimizing.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Here is a complete sample code including constructing the observer and passing it to DMRG:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"using ITensors\n\nmutable struct EntanglementObserver <: AbstractObserver\nend\n\nfunction ITensors.measure!(o::EntanglementObserver; bond, psi, half_sweep, kwargs...)\n wf_center, other = half_sweep==1 ? (psi[bond+1],psi[bond]) : (psi[bond],psi[bond+1])\n U,S,V = svd(wf_center, uniqueinds(wf_center,other))\n SvN = 0.0\n for n=1:dim(S, 1)\n p = S[n,n]^2\n SvN -= p * log(p)\n end\n println(\" Entanglement across bond $bond = $SvN\")\nend\n\nlet\n N = 100\n\n s = siteinds(\"S=1/2\",N)\n\n a = OpSum()\n for n=1:N-1\n a += \"Sz\",n,\"Sz\",n+1\n a += 0.5,\"S+\",n,\"S-\",n+1\n a += 0.5,\"S-\",n,\"S+\",n+1\n end\n H = MPO(a,s)\n psi0 = randomMPS(s,linkdims=4)\n\n nsweeps = 5\n maxdim = [10,20,80,160]\n cutoff = 1E-8\n\n observer = EntanglementObserver()\n\n energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff, observer, outputlevel=2)\n\n return\nend","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Example output:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"...\nSweep 2, half 2, bond (35,36) energy=-44.08644657103751\n Truncated using cutoff=1.0E-08 maxdim=20 mindim=1\n Trunc. err=2.54E-07, bond dimension 20\n Entanglement across bond 35 = 0.7775882479059774\nSweep 2, half 2, bond (34,35) energy=-44.086696891668424\n Truncated using cutoff=1.0E-08 maxdim=20 mindim=1\n Trunc. err=2.12E-07, bond dimension 20\n Entanglement across bond 34 = 0.7103532704635472\nSweep 2, half 2, bond (33,34) energy=-44.08696190368391\n Truncated using cutoff=1.0E-08 maxdim=20 mindim=1\n Trunc. err=1.29E-07, bond dimension 20\n Entanglement across bond 33 = 0.7798362911744212\n...","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"If you only want to see the maximum entanglement during each sweep, you can add a field to the EntanglementObserver object that saves the maximum value encountered so far and keep overwriting this field, printing out the most recently observed maximum at the end of each sweep.","category":"page"},{"location":"examples/DMRG.html#Monitoring-the-Memory-Usage-of-DMRG","page":"DMRG Examples","title":"Monitoring the Memory Usage of DMRG","text":"","category":"section"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"To monitor how much memory (RAM) a DMRG calculation is using while it is running, you can use the Observer system to make a custom observer object that prints out this information. Also the Base.summarysize function, which returns the size in bytes of any Julia object is very helpful here.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"First we define our custom observer type, SizeObserver, and overload the measure! function for it:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"mutable struct SizeObserver <: AbstractObserver\nend\n\nfunction ITensors.measure!(o::SizeObserver; bond, half_sweep, psi, projected_operator, kwargs...)\n if bond==1 && half_sweep==2\n psi_size = Base.format_bytes(Base.summarysize(psi))\n PH_size = Base.format_bytes(Base.summarysize(projected_operator))\n println(\"|psi| = $psi_size, |PH| = $PH_size\")\n end\nend","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"The measure! function grabs certain helpful keywords passed to it by DMRG, checking if bond==1 && half_sweep==2 so that it only runs when at the end of a full sweep.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"When it runs, it calls Base.summarysize on the wavefunction psi object and the projected_operator object. The projected_operator, which is the matrix (Hamiltonian) wrapped into the current MPS basis, is usually the largest-sized object in a DMRG calculation. The code also uses Base.format_bytes to turn an integer representing bytes into a human-readable string.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Here is a complete sample code including constructing the observer and passing it to DMRG:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"using ITensors\n\nmutable struct SizeObserver <: AbstractObserver\nend\n\nfunction ITensors.measure!(o::SizeObserver; bond, sweep, half_sweep, psi, projected_operator, kwargs...)\n if bond==1 && half_sweep==2\n psi_size = Base.format_bytes(Base.summarysize(psi))\n PH_size = Base.format_bytes(Base.summarysize(projected_operator))\n println(\"After sweep $sweep, |psi| = $psi_size, |PH| = $PH_size\")\n end\nend\n\nlet\n N = 100\n\n s = siteinds(\"S=1/2\",N)\n\n a = OpSum()\n for n=1:N-1\n a += \"Sz\",n,\"Sz\",n+1\n a += 0.5,\"S+\",n,\"S-\",n+1\n a += 0.5,\"S-\",n,\"S+\",n+1\n end\n H = MPO(a,s)\n psi0 = randomMPS(s,linkdims=4)\n\n nsweeps = 5\n maxdim = [10,20,80,160]\n cutoff = 1E-8\n\n obs = SizeObserver()\n\n energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff, observer=obs)\n\n return\nend","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Example output:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"After sweep 1, |psi| = 211.312 KiB, |PH| = 593.984 KiB\nAfter sweep 1 energy=-43.95323393592883 maxlinkdim=10 maxerr=8.26E-06 time=0.098\nAfter sweep 2, |psi| = 641.000 KiB, |PH| = 1.632 MiB\nAfter sweep 2 energy=-44.10791340895817 maxlinkdim=20 maxerr=7.39E-07 time=0.132\nAfter sweep 3, |psi| = 1.980 MiB, |PH| = 5.066 MiB\nAfter sweep 3 energy=-44.12593605906466 maxlinkdim=44 maxerr=9.96E-09 time=0.256\nAfter sweep 4, |psi| = 2.863 MiB, |PH| = 7.246 MiB\nAfter sweep 4 energy=-44.127710946536645 maxlinkdim=56 maxerr=9.99E-09 time=0.445\nAfter sweep 5, |psi| = 3.108 MiB, |PH| = 7.845 MiB\nAfter sweep 5 energy=-44.127736798226536 maxlinkdim=57 maxerr=9.98E-09 time=0.564","category":"page"},{"location":"Observer.html#observer","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"","category":"section"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"An observer is an object which can be passed to the ITensor DMRG algorithm, to allow measurements to be performed throughout the DMRG calculation and to set conditions for early stopping of DMRG.","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"The only requirement of an observer is that it is a subtype of AbstractObserver. But to do something interesting, it should also overload at least one the methods measure! or checkdone!.","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"A general purpose observer type called DMRGObserver is included with ITensors which already provides some quite useful features. It accepts a list of strings naming local operators to be measured at each step of DMRG, with the results saved for later analysis. It also accepts an optional energy precision, and stops a DMRG calculation early if the energy no longer changes to this precision. For more details about the DMRGObserver type, see the DMRGObserver documentation page.","category":"page"},{"location":"Observer.html#Defining-a-Custom-Observer","page":"Observer System for DMRG","title":"Defining a Custom Observer","text":"","category":"section"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"To define a custom observer, just make a struct with any name and internal fields you would like, and make this struct a subtype of AbstractObserver.","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"For example, let's make a type called DemoObserver as:","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"mutable struct DemoObserver <: AbstractObserver\n energy_tol::Float64\n last_energy::Float64\n\n DemoObserver(energy_tol=0.0) = new(energy_tol,1000.0)\nend\n","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"In this minimal example, our DemoObserver contains a field energy_tol which we can use to set an early-stopping condition for DMRG, and an field last_energy which our observer will use internally to keep track of changes to the energy after each sweep.","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"Now to give our DemoObserver type a useful behavior we need to define overloads of the methods measure! and checkdone!.","category":"page"},{"location":"Observer.html#Overloading-the-checkdone!-method","page":"Observer System for DMRG","title":"Overloading the checkdone! method","text":"","category":"section"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"Let's start with the checkdone! method. After each sweep of DMRG, the checkdone! method is passed the observer object, as well as a set of keyword arguments which currently include:","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"energy: the current energy\npsi: the current wavefunction MPS\nsweep: the number of the sweep that just finished\noutputlevel: an integer stating the desired level of output","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"If the checkdone! function returns true, then the DMRG routine stops (recall that checkdone! is called only at the end of a sweep).","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"In our example, we will just compare the energy keyword argument to the last_energy variable held inside the DemoObserver:","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"function ITensors.checkdone!(o::DemoObserver;kwargs...)\n sw = kwargs[:sweep]\n energy = kwargs[:energy]\n if abs(energy-o.last_energy)/abs(energy) < o.energy_tol\n println(\"Stopping DMRG after sweep $sw\")\n return true\n end\n # Otherwise, update last_energy and keep going\n o.last_energy = energy\n return false\nend","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"(Recall that in order to properly overload the default behavior, the checkdone! method has to be imported from the ITensors module or preceded with ITensors.)","category":"page"},{"location":"Observer.html#Overloading-the-measure!-method","page":"Observer System for DMRG","title":"Overloading the measure! method","text":"","category":"section"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"The other method that an observer can overload is measure!. This method is called at every step of DMRG, so at every site and for every sweep. The measure! method is passed the current observer object and a set of keyword arguments which include:","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"energy: the energy after the current step of DMRG\npsi: the current wavefunction MPS\nbond: the bond b that was just optimized, corresponding to sites (b,b+1) in the two-site DMRG algorithm\nsweep: the current sweep number\nsweep_is_done: true if at the end of the current sweep, otherwise false\nhalf_sweep: the half-sweep number, equal to 1 for a left-to-right, first half sweep, or 2 for the second, right-to-left half sweep\nspec: the Spectrum object returned from factorizing the local superblock wavefunction tensor in two-site DMRG\noutputlevel: an integer specifying the amount of output to show\nprojected_operator: projection of the linear operator into the current MPS basis","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"For our minimal DemoObserver example here, we will just make a measure! function that prints out some of the information above, but in a more realistic setting one could use the MPS psi to perform essentially arbitrary measurements.","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"function ITensors.measure!(o::DemoObserver; kwargs...)\n energy = kwargs[:energy]\n sweep = kwargs[:sweep]\n bond = kwargs[:bond]\n outputlevel = kwargs[:outputlevel]\n\n if outputlevel > 0\n println(\"Sweep $sweep at bond $bond, the energy is $energy\")\n end\nend","category":"page"},{"location":"Observer.html#Calling-DMRG-with-the-Custom-Observer","page":"Observer System for DMRG","title":"Calling DMRG with the Custom Observer","text":"","category":"section"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"After defining an observer type and overloading at least one of the methods checkdone! or measure! for it, one can construct an object of this type and pass it to the ITensor dmrg function using the observer keyword argument.","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"Continuing with our DemoObserver example above:","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"obs = DemoObserver(1E-4) # use an energy tolerance of 1E-4\nenergy, psi = dmrg(H,psi0,sweeps; observer=obs, outputlevel=1)","category":"page"},{"location":"Observer.html#Complete-Sample-Code","page":"Observer System for DMRG","title":"Complete Sample Code","text":"","category":"section"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"using ITensors\n\nmutable struct DemoObserver <: AbstractObserver\n energy_tol::Float64\n last_energy::Float64\n\n DemoObserver(energy_tol=0.0) = new(energy_tol,1000.0)\nend\n\nfunction ITensors.checkdone!(o::DemoObserver;kwargs...)\n sw = kwargs[:sweep]\n energy = kwargs[:energy]\n if abs(energy-o.last_energy)/abs(energy) < o.energy_tol\n println(\"Stopping DMRG after sweep $sw\")\n return true\n end\n # Otherwise, update last_energy and keep going\n o.last_energy = energy\n return false\nend\n\nfunction ITensors.measure!(o::DemoObserver; kwargs...)\n energy = kwargs[:energy]\n sweep = kwargs[:sweep]\n bond = kwargs[:bond]\n outputlevel = kwargs[:outputlevel]\n\n if outputlevel > 0\n println(\"Sweep $sweep at bond $bond, the energy is $energy\")\n end\nend\n\nlet\n N = 10\n etol = 1E-4\n\n s = siteinds(\"S=1/2\",N)\n\n a = OpSum()\n for n=1:N-1\n a += \"Sz\",n,\"Sz\",n+1\n a += 0.5,\"S+\",n,\"S-\",n+1\n a += 0.5,\"S-\",n,\"S+\",n+1\n end\n H = MPO(a,s)\n psi0 = randomMPS(s,4)\n\n nsweeps = 5\n cutoff = 1E-8\n maxdim = [10,20,100]\n\n obs = DemoObserver(etol)\n\n println(\"Starting DMRG\")\n energy, psi = dmrg(H,psi0; nsweeps, cutoff, maxdim, observer=obs, outputlevel=1)\n\n return\nend","category":"page"},{"location":"examples/ITensor.html#itensor_examples","page":"ITensor Examples","title":"ITensor Code Examples","text":"","category":"section"},{"location":"examples/ITensor.html#Print-Indices-of-an-ITensor","page":"ITensor Examples","title":"Print Indices of an ITensor","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Sometimes the printout of an ITensor can be rather large, whereas you might only want to see its indices. For these cases, just wrap the ITensor in the function inds like this:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"@show inds(T)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"or this","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"println(\"T inds = \",inds(T))","category":"page"},{"location":"examples/ITensor.html#Getting-and-Setting-Elements-of-an-ITensor","page":"ITensor Examples","title":"Getting and Setting Elements of an ITensor","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Say we have an ITensor constructed as:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"i = Index(3,\"index_i\")\nj = Index(2,\"index_j\")\nk = Index(4,\"index_k\")\n\nT = ITensor(i,j,k)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"An ITensor constructed this way starts with all of its elements equal to zero. (Technically it allocates no storage at all but this is an implementation detail.)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Setting Elements","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To set an element of this ITensor, such as the element where (i,j,k) = (2,1,3), you can do the following:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"T[i=>2,j=>1,k=>3] = -3.2","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"In the Julia language, the notation a=>b is a built-in notation for making a Pair(a,b) object.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Because the Index objects are passed to T along with their values, passing them in a different order has exactly the same effect:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"# Both of these lines of code do the same thing:\nT[j=>1,i=>2,k=>3] = -3.2\nT[j=>1,k=>3,i=>2] = -3.2","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Getting Elements","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"You can retrieve individual elements of an ITensor by accessing them through the same notation used to set elements:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"el = T[j=>1,i=>2,k=>3]\nprintln(\"The (i,j,k) = (2,1,3) element of T is \",el)","category":"page"},{"location":"examples/ITensor.html#Making-ITensors-from-Arrays","page":"ITensor Examples","title":"Making ITensors from Arrays","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To initialize all of the elements of an ITensor at once, you can pass a Julia array into the ITensor constructor.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"For example, if we want to construct an ITensor A with indices i,j we can initialize it from a matrix as follows:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"M = [1.0 2.0;\n 3.0 4.0]\n\ni = Index(2,\"i\")\nj = Index(2,\"j\")\n\nA = ITensor(M,i,j)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"More generally we can use an nth-order (n-dimensional) Julia array to initialize an ITensor:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"T = randn(4,7,2)\n\nk = Index(4,\"index_k\")\nl = Index(7,\"index_l\")\nm = Index(2,\"index_m\")\n\nB = ITensor(T,k,l,m)","category":"page"},{"location":"examples/ITensor.html#Making-Arrays-from-ITensors","page":"ITensor Examples","title":"Making Arrays from ITensors","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Not only can we make an ITensor from a Julia array, but we can also convert an ITensor back into a Julia array.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Say we have made an ITensor with two indices:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"using ITensors # hide\nk = Index(4,\"index_k\")\nm = Index(2,\"index_m\")\n\nT = randomITensor(k,m)\n@show T\ndisplay(T) # hide","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Here we used the randomITensor constructor to fill T with random elements but we could make an ITensor some other way too.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Now to convert T into a regular Julia array A, use the Array constructor and pass the indices of T in the order that you want:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"A = Array(T,k,m)\n@show A","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"The reason you have to pass the indices is that the ordering of ITensor indices is an implementation detail and not part of the user interface. So when leaving the ITensor system and converting to a regular array, you must say what ordering of the indices you want. Making the array as A = Array(T,m,k) would give the transpose of the array in the code above.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Note that for efficiency reasons, the array returned by the array function will sometimes be a view of the ITensor, such that changing an element of A would also change the corresponding element of T. This is not always the case though: for example if the indices are passed in a different order from how the internal ITensor storage is arranged, or if T is a block-sparse ITensor, since the (not stored) zero blocks will need to be filled in.","category":"page"},{"location":"examples/ITensor.html#Arithmetic-With-ITensors","page":"ITensor Examples","title":"Arithmetic With ITensors","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"ITensors can be added and subtracted and multiplied by scalars just like plain tensors can. But ITensors have the additional feature that you can add and subtract them even if their indices are in a different order from each other, as long as they have the same collection of indices.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"For example, say we have ITensors A, B, and C:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"i = Index(3,\"i\")\nj = Index(2,\"j\")\nk = Index(4,\"k\")\n\nA = randomITensor(i,j,k)\nB = randomITensor(i,j,k)\nC = randomITensor(k,i,j)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Above we have initialized these ITensors to have random elements, just for the sake of this example.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"We can then add or subtract these ITensors","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"R1 = A + B\nR2 = A - B\nR3 = A + B - C","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"or do more complicated operations involving real and complex scalars too:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"R4 = 2.0*A - B + C/(1+1im)","category":"page"},{"location":"examples/ITensor.html#Elementwise-Operations-on-ITensors","page":"ITensor Examples","title":"Elementwise Operations on ITensors","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"[Note: currently elementwise operations are only defined for dense ITensors, not for block-sparse QN ITensors.]","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"ITensors support Julia broadcasting operations, making it quite easy to carry out element-wise operations on them in a very similar way as for regular Julia arrays. As a concrete example, consider the following ITensor initialized with random elements","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"i = Index(2,\"i\")\nj = Index(3,\"j\")\n\nA = randomITensor(i,j)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Here are some examples of basic element-wise operations we can do using Julia's dotted operator broadcasting syntax.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"# Multiply every element of `A` by 2.0:\nA .*= 2.0","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"# Add 1.5 to every element of A\nA .+= 1.5","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"The dotted notation works for functions too:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"# Replace every element in A by its absolute value:\nA .= abs.(A)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"# Replace every element in A by the number 1.0\nA .= one.(A)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"If have another ITensor B = ITensor(j,i), which has the same set of indices though possibly in a different order, then we can also do element-wise operations involving both ITensors:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"# Add elements of A and B element-wise\nA .= A .+ B\n# Add elements of A and B element-wise with coefficients included\nA .= (2.0 .* A) .+ (-3.0 .* B)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Last but not least, it is possible to make custom functions yourself and broadcast them across elements of ITensors:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"myf(x) = 1.0/(1.0+exp(-x))\nT .= myf.(T)","category":"page"},{"location":"examples/ITensor.html#Making-an-ITensor-with-a-Single-Non-Zero-Element","page":"ITensor Examples","title":"Making an ITensor with a Single Non-Zero Element","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"It is often useful to make ITensors with all elements zero except for a specific element that is equal to 1.0. Use cases can include making product-state quantum wavefunctions or contracting single-element ITensors with other ITensors to set their indices to a fixed value.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To make such an ITensor, use the onehot function. Borrowing terminology from engineering, a \"one hot\" vector or tensor has a single element equal to 1.0 and the rest zero. (In previous versions of ITensor this function was called setelt.)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"The ITensor function onehot takes one or more Index-value Pairs such as i=>2 and j=>1 and returns an ITensor with a 1.0 in the location specified by the Index values:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"using ITensors # hide\ni = Index(2)\nO1 = onehot(i=>1)\nprintln(O1)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"using ITensors # hide\ni = Index(2) # hide\nO2 = onehot(i=>2)\nprintln(O2)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"using ITensors # hide\ni = Index(2) # hide\nj = Index(3)\nT = onehot(i=>2,j=>3)\nprintln(T)","category":"page"},{"location":"examples/ITensor.html#Tracing-an-ITensor","page":"ITensor Examples","title":"Tracing an ITensor","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"An important operation involving a single tensor is tracing out certain pairs of indices. Say we have an ITensor A with indices i,j,l:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"i = Index(4,\"i\")\nj = Index(3,\"j\")\nl = Index(4,\"l\")\n\nA = randomITensor(i,j,l)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"and we want to trace A by summing over the indices i and l locked together, in other words: sum_i A^iji.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To do this in ITensor, we can use a delta tensor, which you can think of as an identity operator or more generally a Kronecker delta or \"hyper-edge\":","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"(Image: )","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Viewed as an array, a delta tensor has all diagonal elements equal to 1.0 and zero otherwise.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Now we can compute the trace by contracting A with the delta tensor:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"trA = A * delta(i,l)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"(Image: )","category":"page"},{"location":"examples/ITensor.html#Factoring-ITensors-(SVD,-QR,-etc.)","page":"ITensor Examples","title":"Factoring ITensors (SVD, QR, etc.)","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"The ITensor approach to tensor factorizations emphasizes the structure of the factorization, and does not require knowing the index ordering.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"ITensor offers various tensor factorizations, such as the singular value decomposition (SVD) and the QR factorization. These are extended to the case of tensors by treating some of the indices as the \"row\" indices and the rest of the indices as the \"column\" indices, reshaping the tensor into a matrix to carry out the factorization, then restoring the tensor structure at the end. All of these steps are done for you by the ITensor system as we will see below.","category":"page"},{"location":"examples/ITensor.html#Singular-Value-Decomposition","page":"ITensor Examples","title":"Singular Value Decomposition","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"The singular value decomposition (SVD) is a matrix factorization that is also extremely useful for general tensors.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"As a brief review, the SVD is a factorization of a matrix M into the product","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"M = U S V^dagger","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"with U and V having the property U^dagger U = 1 and V^dagger V = 1. The matrix S is diagonal and has real, non-negative entries known as the singular values, which are typically ordered from largest to smallest. The SVD is well-defined for any matrix, including rectangular matrices. It also leads to a controlled approximation, where the error due to discarding columns of U and V is small if the corresponding singular values discarded are small.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To compute the SVD of an ITensor, you only need to specify which indices are (collectively) the \"row\" indices (thinking of the ITensor as a matrix), with the rest assumed to be the \"column\" indices.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Say we have an ITensor with indices i,j, and k","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"T = ITensor(i,j,k)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"and we want to treat i and k as the \"row\" indices for the purpose of the SVD.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To perform this SVD, we can call the function svd as follows:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"U,S,V = svd(T,(i,k))","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Diagrammatically the SVD operation above looks like:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"(Image: )","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"The guarantee of the svd function is that the ITensor product U*S*V gives us back an ITensor identical to T:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"@show norm(U*S*V - T) # typical output: norm(U*S*V-T) = 1E-14","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Full working example:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"i = Index(3,\"i\")\nj = Index(4,\"j\")\nk = Index(5,\"k\")\n\nT = randomITensor(i,j,k)\n\nU,S,V = svd(T,(i,k))\n\n@show norm(U*S*V-T)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Truncated SVD","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"An important use of the SVD is approximating a higher-rank tensor by a product of lower-rank tensors whose indices range over only a modest set of values.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To obtain an approximate SVD in ITensor, pass one or more of the following accuracy parameters as named arguments:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"cutoff –- real number epsilon. Discard the smallest singular values lambda_n such that the truncation error is less than epsilon: $ \\frac{\\sum_{n\\in\\text{discarded}} \\lambda^2_n}{\\sum_{n} \\lambda^2_n} < \\epsilon \\:. $ Using a cutoff allows the SVD algorithm to truncate as many states as possible while still ensuring a certain accuracy.\nmaxdim –- integer M. If the number of singular values exceeds M, only the largest M will be retained.\nmindim –- integer m. At least m singular values will be retained, even if some fall below the cutoff","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Let us revisit the example above, but also provide some of these accuracy parameters","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"i = Index(10,\"i\")\nj = Index(40,\"j\")\nk = Index(20,\"k\")\nT = randomITensor(i,j,k)\n\nU,S,V = svd(T,(i,k),cutoff=1E-2)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Note that we have also made the indices larger so that the truncation performed will be non-trivial. In the code above, we specified that a cutoff of epsilon=10^-2 be used. We can check that the resulting factorization is now approximate by computing the squared relative error:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"truncerr = (norm(U*S*V - T)/norm(T))^2\n@show truncerr\n# typical output: truncerr = 8.24E-03","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Note how the computed error is below the cutoff epsilon we requested.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Full working example including truncation:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"i = Index(10,\"i\");\nj = Index(40,\"j\");\nk = Index(20,\"k\");\n\nT = randomITensor(i,j,k)\n\nU,S,V = svd(T,(i,k),cutoff=1E-2)\n\n@show norm(U*S*V-T)\n@show (norm(U*S*V - T)/norm(T))^2","category":"page"},{"location":"examples/ITensor.html#QR-Factorization","page":"ITensor Examples","title":"QR Factorization","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Computing the QR factorization of an ITensor works in a similar way as for the SVD. In addition to passing the ITensor you want to factorize, you must also pass the indices you want to end up on the tensor Q, in other words to be treated as the \"row\" indices for the purpose of defining the QR factorization.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Say we want to compute the QR factorization of an ITensor T with indices i,j,k, putting the indices i and k onto Q and the remaining indices onto R. We can do this as follows:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"(Image: )","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"T = randomITensor(i,j,k)\nQ,R = qr(T,(i,k);positive=true)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Note the use of the optional positive=true keyword argument, which ensures that the diagonal elements of R are non-negative. With this option, the QR factorization is unique, which can be useful in certain cases.","category":"page"},{"location":"examples/ITensor.html#Combining-Multiple-Indices-into-One-Index","page":"ITensor Examples","title":"Combining Multiple Indices into One Index","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"It can be very useful to combine or merge multiple indices of an ITensor into a single Index. Say we have an ITensor with indices i,j,k and we want to combine Index i and Index k into a new Index. This new Index (call it c) will have a dimension whose size is the dimension of i times the dimension of k.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To carry out this procedure we can make a special kind of ITensor: a combiner. To make a combiner, call the function combiner, passing the indices you want to combine:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"using ITensors # hide\ni = Index(4,\"i\") # hide\nj = Index(3,\"j\") # hide\nk = Index(2,\"k\") # hide\nC = combiner(i,k; tags=\"c\")\nnothing # hide","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Then if we have an ITensor","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"T = randomITensor(i,j,k)\n@show inds(T)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"we can combine indices i and k by contracting with the combiner:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"CT = C * T\nnothing # hide","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Printing out the indices of the new ITensor CT we can see that it has only two indices:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"@show inds(CT)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"The first is the newly made combined Index, which was made for us by the combiner function and the second is the j Index of T which was not part of the combining process. To access the combined Index you can call the combinedind function on the combiner:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"ci = combinedind(C)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"We can visualize all of the steps above as follows: (Image: )","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Combining is not limited to two indices and you can combine any number of indices, in any order, using a combiner.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To undo the combining process and uncombine the Index c back into i,k, just contract with the conjugate of the combiner ITensor dag(C).","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"UT = dag(C) * CT\n@show inds(UT)","category":"page"},{"location":"examples/ITensor.html#Write-and-Read-an-ITensor-to-Disk-with-HDF5","page":"ITensor Examples","title":"Write and Read an ITensor to Disk with HDF5","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"info: Info\nMake sure to install the HDF5 package to use this feature. (Run julia> ] add HDF5 in the Julia REPL console.)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Saving ITensors to disk can be very useful. For example, you might encounter a bug in your own code, and by reading the ITensors involved from disk you can shortcut the process of running a lengthy algorithm over many times to reproduce the bug. Or you can save the output of an expensive calculation, such as a DMRG calculation, and use it as a starting point for multiple follow-up calculations such as computing time-dependent properties.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"ITensors can be written to files using the HDF5 format. HDF5 offers many benefits such as being portable across different machine types, and offers a standard interface across various libraries and languages.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Writing an ITensor to an HDF5 File","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Let's say you have an ITensor T which you have made or obtained from a calculation. To write it to an HDF5 file named \"myfile.h5\" you can use the following pattern:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"using HDF5\nf = h5open(\"myfile.h5\",\"w\")\nwrite(f,\"T\",T)\nclose(f)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Above, the string \"T\" can actually be any string you want such as \"ITensor T\" or \"Result Tensor\" and doesn't have to have the same name as the reference T. Closing the file f is optional and you can also write other objects to the same file before closing it.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Reading an ITensor from an HDF5 File","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Say you have an HDF5 file \"myfile.h5\" which contains an ITensor stored as a dataset with the name \"T\". (Which would be the situation if you wrote it as in the example above.) To read this ITensor back from the HDF5 file, use the following pattern:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"using HDF5\nf = h5open(\"myfile.h5\",\"r\")\nT = read(f,\"T\",ITensor)\nclose(f)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Note the ITensor argument to the read function, which tells Julia which read function to call and how to interpret the data stored in the HDF5 dataset named \"T\". In the future we might lift the requirement of providing the type and have it be detected automatically from the data stored in the file.","category":"page"},{"location":"examples/Physics.html#Physics-(SiteType)-System-Examples","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"","category":"section"},{"location":"examples/Physics.html#Obtaining-a-Predefined-Operator","page":"Physics (SiteType) System Examples","title":"Obtaining a Predefined Operator","text":"","category":"section"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Given an Index carrying a \"physical\" tag such as \"Qubit\", \"S=1/2\", \"Boson\", etc. there are a set of pre-defined operators for each tag. The entire set of operators can be found in the section SiteTypes Included with ITensor.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"If you have an Index s carrying a \"S=1/2\" tag, for example, you can obtain the \"Sz\" operator like this:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"op(\"Sz\",s)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Usually indices with physical tags come from an array of indices returned from the siteinds function","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"sites = siteinds(\"S=1/2\",N)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"in which case one might want the \"Sz\" operator on site 4","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Sz4 = op(\"Sz\",sites[4])","category":"page"},{"location":"examples/Physics.html#Make-a-Custom-Operator-from-a-Matrix","page":"Physics (SiteType) System Examples","title":"Make a Custom Operator from a Matrix","text":"","category":"section"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The op function can be passed any matrix, as long as it has the correct dimensions, and it will make this into an ITensor representing the operator with the corresponding matrix elements.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"For example, if we have a two-dimensional Index s we could make the \"Sz\" operator ourselves from the matrix","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"M = [1/2 0 ; 0 -1/2]","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"by calling","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Sz = op(M,s)","category":"page"},{"location":"examples/Physics.html#custom_op","page":"Physics (SiteType) System Examples","title":"Making a Custom op Definition","text":"","category":"section"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The function op is used to obtain operators defined for a given \"site type\". ITensor includes pre-defined site types such as \"S=1/2\", \"S=1\", \"Electron\" and others. Or you can define your own site type as discussed in detail in the code examples further below.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Extending op Function Definitions","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Perhaps the most common part of the site type system one wishes to extend are the various op or op! function overloads which allow code like","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"s = siteind(\"S=1/2\")\nSz = op(\"Sz\",s)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"to automatically create the S^z operator for an Index s based on the \"S=1/2\" tag it carries. A major reason to define such op overloads is to allow the OpSum system to recognize new operator names, as discussed more below.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Let's see how to introduce a new operator name into the ITensor site type system for this existing site type of \"S=1/2\". The operator we will introduce is the projector onto the up spin state P_uparrow which we will denote with the string \"Pup\".","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"As a matrix acting on the space uparrowrangle downarrowrangle , the P_uparrow operator is given by","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"beginaligned\n\nP_uparrow =\nbeginbmatrix\n 1 0 \n 0 0 \nendbmatrix\n\nendaligned","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"To add this operator to the ITensor op system, we just need to introduce the following code","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"using ITensors\n\nITensors.op(::OpName\"Pup\",::SiteType\"S=1/2\") =\n [1 0\n 0 0]","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"This code can be defined anywhere, such as in your own personal application code and does not have to be put into the ITensor library source code.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Note that we have to name the function ITensors.op and not just op so that it overloads other functions of the name op inside the ITensors module.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Having defined the above code, we can now do things like","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"s = siteind(\"S=1/2\")\nPup = op(\"Pup\",s)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"to obtain the \"Pup\" operator for our \"S=1/2\" Index s. Or we can do a similar thing for an array of site indices:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"N = 40\ns = siteinds(\"S=1/2\",N)\nPup1 = op(\"Pup\",s[1])\nPup3 = op(\"Pup\",s[3])","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Note that for the \"Qudit\"/\"Boson\" site types, you have to define your overload of op with the dimension of the local Hilbert space, for example:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"using ITensors\n\nfunction ITensors.op(::OpName\"P1\", ::SiteType\"Boson\", d::Int)\n o = zeros(d, d)\n o[1, 1] = 1\n return o\nend","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Alternatively you could use Julia's array comprehension syntax:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"ITensors.op(::OpName\"P1\", ::SiteType\"Boson\", d::Int) =\n [(i == j == 1) ? 1.0 : 0.0 for i in 1:d, j in 1:d]","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Using Custom Operators in OpSum","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"A key use of these op system extensions is allowing additional operator names to be recognized by the OpSum system for constructing matrix product operator (MPO) tensor networks. With the code above defining the \"Pup\" operator, we are now allowed to use this operator name in any OpSum code involving \"S=1/2\" site indices.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"For example, we could now make an OpSum involving our custom operator such as:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"N = 100\nsites = siteinds(\"S=1/2\",N)\nos = OpSum()\nfor n=1:N\n os += \"Pup\",n\nend\nP = MPO(os,sites)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"This code makes an MPO P which is just the sum of a spin-up projection operator acting on every site.","category":"page"},{"location":"examples/Physics.html#Making-a-Custom-state-Definition","page":"Physics (SiteType) System Examples","title":"Making a Custom state Definition","text":"","category":"section"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The function state is used to define states (single-site wavefunctions) that sites can be in. For example, the \"Qubit\" site type includes definitions for the \"0\" and \"1\" states as well as the \"+\" (eigenstate of X operator) state. The \"S=1/2\" site type includes definitions for the \"Up\" and \"Dn\" (down) states.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Say we want to define a new state for the \"Electron\" site type called \"+\", which has the meaning of one electron with its spin in the +X direction. First let's review the existing state definitions:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"ITensors.state(::StateName\"Emp\", ::SiteType\"Electron\") = [1.0, 0, 0, 0]\nITensors.state(::StateName\"Up\", ::SiteType\"Electron\") = [0.0, 1, 0, 0]\nITensors.state(::StateName\"Dn\", ::SiteType\"Electron\") = [0.0, 0, 1, 0]\nITensors.state(::StateName\"UpDn\", ::SiteType\"Electron\") = [0.0, 0, 0, 1]","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"As we can see, the four settings of an \"Electron\" index correspond to the states 0rangle uparrowrangle downarrowrangle uparrowdownarrowrangle.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"So we can define our new state \"+\" as follows:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"ITensors.state(::StateName\"+\", ::SiteType\"Electron\") = [0, 1/sqrt(2), 1/sqrt(2), 0]","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"which makes the state","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"+rangle = frac1sqrt2 uparrowrangle + frac1sqrt2 downarrowrangle","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Having defined this overload of state, if we have an Index of type \"Electron\" we can obtain our new state for it by doing","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"s = siteind(\"Electron\")\nplus = state(\"+\",s)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"We can also use this new state definition in other ITensor features such as the MPS constructor taking an array of state names.","category":"page"},{"location":"examples/Physics.html#Make-a-Custom-Local-Hilbert-Space-/-Physical-Degree-of-Freedom","page":"Physics (SiteType) System Examples","title":"Make a Custom Local Hilbert Space / Physical Degree of Freedom","text":"","category":"section"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"ITensor provides support for a range of common local Hilbert space types, or physical degrees of freedom, such as S=1/2 and S=1 spins; spinless and spinful fermions; and more.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"However, there can be many cases where you need to make custom degrees of freedom. You might be working with an exotic system, such as Z_N parafermions for example, or need to customize other defaults provided by ITensor.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"In ITensor, such a customization is done by overloading functions on specially designated Index tags. Below we give an brief introduction by example of how to make such custom Index site types in ITensor. Other code formulas following this one explain how to build on this example to expand the capabilities of your custom site type such as adding support for quantum number (QN) conservation and defining custom mappings of strings to states.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Throughout we will focus on the example of S=32 spins. These are spins taking the S^z values of +32+12-12-32. So as tensor indices, they are indices of dimension 4.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The key operators we will make for this example are S^z, S^+, and S^-, which are defined as:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"beginaligned\nS^z =\nbeginbmatrix\n32 0 0 0 \n 0 12 0 0 \n 0 0 -12 0 \n 0 0 0 -32\nendbmatrix \n\nS^+ =\nbeginbmatrix\n 0 sqrt3 0 0 \n 0 0 2 0 \n 0 0 0 sqrt3 \n 0 0 0 0 \nendbmatrix \n\nS^- =\nbeginbmatrix\n 0 0 0 0 \n sqrt3 0 0 0 \n 0 2 0 0 \n 0 0 sqrt3 0 \nendbmatrix \nendaligned","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Code Preview","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"First let's see the minimal code needed to define and use this new S=32 site type, then we will discuss what each part of the code is doing.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"using ITensors\n\nITensors.space(::SiteType\"S=3/2\") = 4\n\nITensors.op(::OpName\"Sz\",::SiteType\"S=3/2\") =\n [+3/2 0 0 0\n 0 +1/2 0 0\n 0 0 -1/2 0\n 0 0 0 -3/2]\n\nITensors.op(::OpName\"S+\",::SiteType\"S=3/2\") =\n [0 √3 0 0\n 0 0 2 0\n 0 0 0 √3\n 0 0 0 0]\n\nITensors.op(::OpName\"S-\",::SiteType\"S=3/2\") =\n [0 0 0 0\n √3 0 0 0\n 0 2 0 0\n 0 0 √3 0]\n","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Now let's look at each part of the code above.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The SiteType","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The most important aspect of this code is a special type, known as a SiteType, which is a type made from a string. The string of interest here will be an Index tag. In the code above, the SiteType we are using is","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"SiteType\"S=3/2\"","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"What is the purpose of a SiteType? The answer is that we would like to be able to select different functions to call on an ITensor Index based on what tags it has, but that is not directly possible in Julia or indeed most languages. However, if we can map a tag to a type in the Julia type system, we can create function overloads for that type. ITensor does this for certain functions for you, and we will discuss a few of these functions below. So if the code encounters an Index such as Index(4,\"S=3/2\") it can call these functions which are specialized for indices carrying the \"S=3/2\" tag.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The space Function","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"One of the overloadable SiteType functions is space, whose job is to describe the vector space corresponding to that site type. For our SiteType\"S=3/2\" overload of space, which gets called for any Index carrying the \"S=3/2\" tag, the definition is","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"ITensors.space(::SiteType\"S=3/2\") = 4","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Note that the function name is prepended with ITensors. before space. This prefix makes sure the function is overloading other versions of the space inside the ITensors module.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The only information needed about the vector space of a \"S=3/2\" Index in this example is that it is of dimension four. So the space function returns the integer 4. We will see in more advanced examples that the returned value can instead be an array which specifies not only the dimension of a \"S=3/2\" Index, but also additional subspace structure it has corresponding to quantum numbers.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"After defining this space function, you can just write code like:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"s = siteind(\"S=3/2\")","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"to obtain a single \"S=3/2\" Index, or write code like","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"N = 100\nsites = siteinds(\"S=3/2\",N)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"to obtain an array of N \"S=3/2\" indices. The custom space function will be used to determine the dimension of these indices, and the siteind or siteinds functions provided by ITensor will help with extra things like putting other Index tags that are conventional for site indices.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The op Function","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The op function lets you define custom local operators associated to the physical degrees of freedom of your SiteType. Then for example you can use indices carrying your custom tag with OpSum and the OpSum system will know how to automatically convert names of operators such as \"Sz\" or \"S+\" into ITensors so that it can make an actual MPO.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"In our example above, we defined this function for the case of the \"Sz\" operator as:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"using ITensors # hide\n\nITensors.op(::OpName\"Sz\",::SiteType\"S=3/2\") =\n [+3/2 0 0 0\n 0 +1/2 0 0\n 0 0 -1/2 0\n 0 0 0 -3/2]","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"As you can see, the function is passed two objects: an OpName and a SiteType. The strings \"Sz\" and \"S=3/2\" are also part of the type of these objects, and have the meaning of which operator name we are defining and which site type these operators are defined for.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The body of this overload of ITensors.op constructs and returns a Julia matrix which gives the matrix elements of the operator we are defining.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Once this function is defined, and if you have an Index such as","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"s = Index(4,\"S=3/2\")","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"then, for example, you can get the \"Sz\" operator for this Index and print it out by doing:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Sz = op(\"Sz\",s)\nprintln(Sz)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Again, through the magic of the SiteType system, the ITensor library takes your Index, reads off its tags, notices that one of them is \"S=3/2\", and converts this into the type SiteType\"S=3/2\" in order to call the specialized function ITensors.op defined above.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"You can use the op function yourself with a set of site indices created from the siteinds function like this:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"N = 100\nsites = siteinds(\"S=3/2\",N)\nSz1 = op(\"Sz\",sites[1])\nSp3 = op(\"S+\",sites[3])","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Alternatively, you can write the lines of code above in the style of Sz1 = op(\"Sz\",sites,1).","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"This same op function is used inside of OpSum (formerly called AutoMPO) when it converts its input into an actual MPO. So by defining custom operator names you can pass any of these operator names into OpSum and it will know how to use these operators.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Further Steps","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"See how the built-in site types are defined inside the ITensor library:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"S=1/2 sites - Dimension 2 local Hilbert space. Similar to the \"Qubit\" site type, shares many of the same operator definitions.\nQubit sites - Dimension 2 local Hilbert space. Similar to the \"S=1/2\" site type, shares many of the same operator definitions.\nS=1 sites - Dimension 3 local Hilbert space.\nFermion sites - Dimension 2 local Hilbert space. Spinless fermion site type.\nElectron sites - Dimension 4 local Hilbert space. Spinfull fermion site type.\ntJ sites - Dimension 3 local Hilbert space. Spinfull fermion site type but without a doubly occupied state in the Hilbert space.\nBoson sites - General d-dimensional local Hilbert space. Shares the same operator definitions as the \"Qudit\" site type.\nQudit sites - General d-dimensional local Hilbert space. Generalization of the \"Qubit\" site type, shares the same operator definitions as the Boson site type.","category":"page"},{"location":"examples/Physics.html#Make-a-Custom-Local-Hilbert-Space-with-QNs","page":"Physics (SiteType) System Examples","title":"Make a Custom Local Hilbert Space with QNs","text":"","category":"section"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"In the previous example above, we discussed the basic, minimal code needed to define a custom local Hilbert space, using the example of a S=32 spin Hilbert space. In those examples, the space function defining the vector space of a S=32 spin only provides the dimension of the space. But the Hilbert space of a S=32 spin has additional structure, which is that each of its four subspaces (each of dimension 1) can be labeled by a different S^z quantum number.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"In this code formula we will include this extra quantum information in the definition of the space of a S=32 spin.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Code Preview","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"First let's see the minimal code needed to add the option for including quantum numbers of our S=32 site type, then we will discuss what each part of the code is doing.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"using ITensors\n\nfunction ITensors.space(::SiteType\"S=3/2\";\n conserve_qns=false)\n if conserve_qns\n return [QN(\"Sz\",3)=>1,QN(\"Sz\",1)=>1,\n QN(\"Sz\",-1)=>1,QN(\"Sz\",-3)=>1]\n end\n return 4\nend\n\nITensors.op(::OpName\"Sz\",::SiteType\"S=3/2\") =\n [+3/2 0 0 0\n 0 +1/2 0 0\n 0 0 -1/2 0\n 0 0 0 -3/2]\n\nITensors.op(::OpName\"S+\",::SiteType\"S=3/2\") =\n [0 √3 0 0\n 0 0 2 0\n 0 0 0 √3\n 0 0 0 0]\n\nITensors.op(::OpName\"S-\",::SiteType\"S=3/2\") =\n [0 0 0 0\n √3 0 0 0\n 0 2 0 0\n 0 0 √3 0]\n\n","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Now let's look at each part of the code above.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The space function","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"In the previous code example above, we discussed that the function space tells the ITensor library the basic information about how to construct an Index associated with a special Index tag, in this case the tag \"S=3/2\". As in that code formula, if the user does not request that quantum numbers be included (the case conserve_qns=false) then all that the space function returns is the number 4, indicating that a \"S=3/2\" Index should be of dimension 4.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"But if the conserve_qns keyword argument gets set to true, the space function we defined above returns an array of QN=>Int pairs. (The notation a=>b in Julia constructs a Pair object.) Each pair in the array denotes a subspace. The QN part of each pair says what quantum number the subspace has, and the integer following it indicates the dimension of the subspace.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"After defining the space function this way, you can write code like:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"s = siteind(\"S=3/2\"; conserve_qns=true)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"to obtain a single \"S=3/2\" Index which carries quantum number information. The siteind function built into ITensor relies on your custom space function to ask how to construct a \"S=3/2\" Index but also includes some other Index tags which are conventional for all site indices.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"You can now also call code like:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"N = 100\nsites = siteinds(\"S=3/2\",N; conserve_qns=true)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"to obtain an array of N \"S=3/2\" indices which carry quantum numbers.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The op Function in the Quantum Number Case","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Note that the op function overloads are exactly the same as for the more basic case of defining an \"S=3/2\" Index type that does not carry quantum numbers. There is no need to upgrade any of the op functions for the QN-conserving case. The reason is that all QN, block-sparse information about an ITensor is deduced from the indices of the tensor, and setting elements of such tensors does not require any other special code.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"However, only operators which have a well-defined QN flux–-meaning they always change the quantum number of a state they act on by a well-defined amount–-can be used in practice in the case of QN conservation. Attempting to build an operator, or any ITensor, without a well-defined QN flux out of QN-conserving indices will result in a run time error. An example of an operator that would lead to such an error would be the \"Sx\" spin operator since it alternately increases S^z or decreases S^z depending on the state it acts on, thus it does not have a well-defined QN flux. But it is perfectly fine to define an op overload for the \"Sx\" operator and to make this operator when working with dense, non-QN-conserving ITensors or when S^z is not conserved.","category":"page"},{"location":"RunningOnGPUs.html#Running-on-GPUs","page":"Running on GPUs","title":"Running on GPUs","text":"","category":"section"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"ITensor provides package extensions for running tensor operations on a variety of GPU backends. You can activate a backend by loading the appropriate Julia GPU package alongside ITensors.jl and moving your tensors and/or tensor networks to an available GPU using that package's provided conversion functions.","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"For example, you can load CUDA.jl to perform tensor operations on NVIDIA GPUs or Metal.jl to perform tensor operations on Apple GPUs:","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"using ITensors\n\ni, j, k = Index.((2, 2, 2))\nA = randomITensor(i, j)\nB = randomITensor(j, k)\n\n# Perform tensor operations on CPU\nA * B\n\n###########################################\nusing CUDA # This will trigger the loading of `NDTensorsCUDAExt` in the background\n\n# Move tensors to NVIDIA GPU\nAcu = cu(A)\nBcu = cu(B)\n\n# Perform tensor operations on NVIDIA GPU\nAcu * Bcu\n\n###########################################\nusing Metal # This will trigger the loading of `NDTensorsMetalExt` in the background\n\n# Move tensors to Apple GPU\nAmtl = mtl(A)\nBmtl = mtl(B)\n\n# Perform tensor operations on Apple GPU\nAmtl * Bmtl","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"Note that we highly recommend using these new package extensions as opposed to ITensorGPU.jl, which is ITensor's previous CUDA backend. The package extensions are better integrated into the main library so are more reliable and better supported right now. We plan to deprecate ITensorGPU.jl in the future.","category":"page"},{"location":"RunningOnGPUs.html#GPU-backends","page":"Running on GPUs","title":"GPU backends","text":"","category":"section"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"ITensor currently provides package extensions for the following GPU backends:","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"CUDA.jl (NVIDIA GPUs)\nMetal.jl (Apple GPUs)\nAMDGPU.jl (AMD GPUs)","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"Our goal is to support all GPU backends which are supported by the JuliaGPU organization.","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"Some important caveats to keep in mind related to the ITensor GPU backends are:","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"only dense tensor operations are well supported right now. Block sparse operations (which arise when QN conservation is enabled) are under active development and either may not work or may be slower than their CPU counterparts,\ncertain GPU backends do not have native support for certain matrix decompositions like svd, eigen, and qr in which case we will perform those operations on CPU. If your calculation is dominated by those operations, there likely is no advantage to running it on GPU right now. CUDA generally has good support for native matrix decompositions, while Metal and AMD have more limited support right now, and\nsingle precision (Float32) calculations are generally fastest on GPU.","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"The table below summarizes each backend's current capabilities.","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":" CUDA ROCm Metal oneAPI\nContractions (dense) ✓ ✓ ✓ N/A\nContractions (cuTENSOR) In progress N/A N/A N/A\nQR (dense) ✓ On CPU On CPU N/A\nSVD (dense) ✓ On CPU On CPU N/A\nEigendecomposition (dense) ✓ On CPU On CPU N/A\nDouble precision (Float64) ✓ ✓ N/A N/A\nBlock sparse In progress In progress In progress N/A","category":"page"},{"location":"IncludedSiteTypes.html#SiteTypes-Included-with-ITensor","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"","category":"section"},{"location":"IncludedSiteTypes.html#\"S1/2\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"S=1/2\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Site indices with the \"S=1/2\" site type represent S=12 spins with the states uparrowrangle, downarrowrangle.","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Making a single \"S=1/2\" site or collection of N \"S=1/2\" sites","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"s = siteind(\"S=1/2\")\nsites = siteinds(\"S=1/2\",N)","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"conserve_qns (default: false): conserve total S^z\nconserve_sz (default: conserve_qns): conserve total S^z\nconserve_szparity (default: false): conserve total S^z modulo two\nqnname_sz (default: \"Sz\"): name of total S^z QN\nqnname_szparity (default: \"SzParity\"): name of total S^z modulo two QN","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"For example:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"sites = siteinds(\"S=1/2\",N; conserve_szparity=true, qnname_szparity=\"SzP\")","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Operators associated with \"S=1/2\" sites can be made using the op function, for example","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Sz = op(\"Sz\",s)\nSz4 = op(\"Sz\",sites[4])","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available operators are exactly the same as those for the \"Qubit\" site type. Please see the list of \"Qubit\" operators below.","category":"page"},{"location":"IncludedSiteTypes.html#\"Qubit\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"Qubit\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Site indices with the \"Qubit\" site type represent qubits with the states 0rangle, 1rangle.","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Making a single \"Qubit\" site or collection of N \"Qubit\" sites","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"s = siteind(\"Qubit\")\nsites = siteinds(\"Qubit\",N)","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"conserve_qns (default: false): conserve total qubit parity\nconserve_parity (default: conserve_qns): conserve total qubit parity\nconserve_number (default: false): conserve total qubit number\nqnname_parity (default: \"Parity\"): name of total qubit parity QN\nqnname_number (default: \"Number\"): name of total qubit number QN","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"For example:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"sites = siteinds(\"Qubit\",N; conserve_parity=true)","category":"page"},{"location":"IncludedSiteTypes.html#\"Qubit\"-and-\"S1/2\"-States","page":"SiteTypes Included with ITensor","title":"\"Qubit\" and \"S=1/2\" States","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"The available state names for \"Qubit\" sites are:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"0\" (aliases: \"Z+\", \"Zp\", \"Up\", \"↑\") Qubit in the 0 state\n\"1\" (aliases: \"Z-\", \"Zm\", \"Dn\", \"↓\") Qubit in the 1 state\n\"+\" (aliases: \"X+\", \"Xp\") Qubit in the +rangle state (+1 eigenvector of sigma_x)\n\"+\" (aliases: \"X-\", \"Xm\") Qubit in the -rangle state (-1 eigenvector of sigma_x)\n\"i\" (aliases: \"Y+\", \"Yp\") Qubit in the irangle state (+1 eigenvector of sigma_y)\n\"-i\" (aliases: \"Y-\", \"Ym\") Qubit in the -irangle state (+1 eigenvector of sigma_y)","category":"page"},{"location":"IncludedSiteTypes.html#\"Qubit\"-and-\"S1/2\"-Operators","page":"SiteTypes Included with ITensor","title":"\"Qubit\" and \"S=1/2\" Operators","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Operators or gates associated with \"Qubit\" sites can be made using the op function, for example","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"H = op(\"H\",s)\nH3 = op(\"H\",sites[3])","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Single-qubit operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"X\" (aliases: \"σx\", \"σ1\") Pauli X operator\n\"Y\" (aliases: \"σy\", \"σ2\") Pauli Y operator\n\"iY\" (aliases: \"iσy\", \"iσ2\") Pauli Y operator times i\n\"Z\" (aliases: \"σz\", \"σ3\") Pauli Z operator\n\"√NOT\" (aliases: \"X\")\n\"H\" Hadamard gate\n\"Phase\" (takes optional argument: ϕ=π/2) (aliases: \"P\", \"S\")\n\"π/8\" (aliases: \"T\")\n\"Rx\" (takes argument: θ) Rotation around x axis\n\"Ry\" (takes argument: θ) Rotation around y axis\n\"Rz\" (takes argument: θ) Rotation around z axis\n\"Rn\" (takes arguments: θ, ϕ, λ) (aliases: \"Rn̂\") Rotation about axis n=(θ, ϕ, λ)\n\"Proj0\" (aliases: \"ProjUp\", \"projUp\") Operator 0ranglelangle 0\n\"Proj1\" (aliases: \"ProjDn\", \"projDn\") Operator 1ranglelangle 1","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Spin operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Sz\" (aliases: \"Sᶻ\") Spin z operator S^z = frac12 sigma_z\n\"S+\" (alises: \"S⁺\", \"Splus\") Raising operator S^+ = S^x + iS^y\n\"S-\" (aliases: \"S⁻\", \"Sminus\") Lowering operator S^- = S^x - iS^y\n\"Sx\" (alises: \"Sˣ\") Spin x operator S^x = frac12 sigma_x\n\"iSy\" (aliases: \"iSʸ\") i times spin y operator iS^y = fraci2 sigma_y\n\"Sy\" (aliases: \"Sʸ\") Spin y operator S^y = frac12 sigma_y\n\"S2\" (aliases: \"S²\"`) Square of spin vector operator S^2=vecScdotvecS=frac34 I\n\"ProjUp\" (aliases: \"projUp\", \"Proj0\") Operator ranglelangle \n\"ProjDn\" (aliases: \"projDn\", \"Proj1\") Operator ranglelangle ","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Two-qubit gates:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"CNOT\" (aliases: \"CX\") Controlled NOT gate\n\"CY\" Controlled Y gate\n\"CZ\" Controlled Z gate\n\"CPHASE\" (aliases: \"Cphase\") Controlled Phase gate\n\"CRx\" (aliases: \"CRX\") (takes arguments: θ)\n\"CRy\" (aliases: \"CRY\") (takes arguments: θ)\n\"CRz\" (aliases: \"CRZ\") (takes arguments: θ)\n\"CRn\" (aliases: \"CRn̂\") (takes arguments: θ, ϕ, λ)\n\"SWAP\" (aliases: \"Swap\")\n\"√SWAP\" (aliases: \"√Swap\")\n\"iSWAP\" (aliases: \"iSwap\")\n\"√iSWAP\" (aliases: \"√iSwap\")\n\"Rxx\" (aliases: \"RXX\") (takes arguments: ϕ) Ising (XX) coupling gate\n\"Ryy\" (aliases: \"RYY\") (takes arguments: ϕ) Ising (YY) coupling gate\n\"Rzz\" (aliases: \"RZZ\") (takes arguments: ϕ) Ising (ZZ) coupling gate","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Three-qubit gates:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Toffoli\" (aliases \"CCNOT\", \"CCX\", \"TOFF\")\n\"Fredkin\" (aliases \"CSWAP\", \"CSwap\", \"CS\")","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Four-qubit gates:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"CCCNOT\"","category":"page"},{"location":"IncludedSiteTypes.html#\"S1\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"S=1\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Site indices with the \"S=1\" site type represent S=1 spins with the states uparrowrangle, 0rangle, downarrowrangle.","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Making a single \"S=1\" site or collection of N \"S=1\" sites","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"s = siteind(\"S=1\")\nsites = siteinds(\"S=1\",N)","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"conserve_qns (default: false): conserve total S^z\nconserve_sz (default: conserve_qns): conserve total S^z\nqnname_sz (default: \"Sz\"): name of total S^z QN","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"For example:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"sites = siteinds(\"S=1\",N; conserve_sz=true, qnname_sz=\"TotalSz\")","category":"page"},{"location":"IncludedSiteTypes.html#\"S1\"-States","page":"SiteTypes Included with ITensor","title":"\"S=1\" States","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"The available state names for \"S=1\" sites are:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Up\" (aliases: \"Z+\", \"↑\") spin in the up state\n\"Z0\" (aliases: \"0\") spin in the Sz=0 state\n\"Dn\" (aliases: \"Z-\", \"↓\") spin in the Sz=0 state","category":"page"},{"location":"IncludedSiteTypes.html#\"S1\"-Operators","page":"SiteTypes Included with ITensor","title":"\"S=1\" Operators","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Operators associated with \"S=1\" sites can be made using the op function, for example","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Sz = op(\"Sz\",s)\nSz4 = op(\"Sz\",sites[4])","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Spin operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Sz\" (aliases: \"Sᶻ\")\n\"Sz2\" Square of S^z operator\n\"S+\" (alises: \"S⁺\", \"Splus\")\n\"S-\" (aliases: \"S⁻\", \"Sminus\")\n\"Sx\" (alises: \"Sˣ\")\n\"Sx2\" Square of S^x operator\n\"iSy\" (aliases: \"iSʸ\")\n\"Sy\" (aliases: \"Sʸ\")\n\"Sy2\" Square of S^y operator\n\"S2\" (aliases: \"S²\"`)","category":"page"},{"location":"IncludedSiteTypes.html#\"Boson\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"Boson\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"The \"Boson\" site type is an alias for the \"Qudit\" site type. Please see more information about \"Qudit\" below:","category":"page"},{"location":"IncludedSiteTypes.html#\"Qudit\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"Qudit\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Making a single \"Qudit\" site or collection of N \"Qudit\" sites","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"s = siteind(\"Qudit\")\nsites = siteinds(\"Qudit\",N)","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"dim (default: 2): dimension of the index (number of qudit or boson values)\nconserve_qns (default: false): conserve total qudit or boson number\nconserve_number (default: conserve_qns): conserve total qudit or boson number\nqnname_number (default: \"Number\"): name of total qudit or boson number QN","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"For example:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"sites = siteinds(\"Qudit\",N; conserve_number=true)","category":"page"},{"location":"IncludedSiteTypes.html#\"Qudit\"-and-\"Boson\"-Operators","page":"SiteTypes Included with ITensor","title":"\"Qudit\" and \"Boson\" Operators","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Operators associated with \"Qudit\" sites can be made using the op function, for example","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"A = op(\"A\",s)\nA4 = op(\"A\",sites[4])","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Single-qudit operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"A\" (aliases: \"a\")\n\"Adag\" (aliases: \"adag\", \"a†\")\n\"N\" (aliases: \"n\")","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Two-qudit operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"ab\"\n\"a†b\"\n\"ab†\"\n\"a†b†\"","category":"page"},{"location":"IncludedSiteTypes.html#\"Fermion\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"Fermion\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Site indices with the \"Fermion\" SiteType represent spinless fermion sites with the states 0rangle, 1rangle, corresponding to zero fermions or one fermion.","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Making a single \"Fermion\" site or collection of N \"Fermion\" sites","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"s = siteind(\"Fermion\")\nsites = siteinds(\"Fermion\",N)","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"conserve_qns (default: false): conserve total number of fermions\nconserve_nf (default: conserve_qns): conserve total number of fermions\nconserve_nfparity (default: conserve_qns): conserve total fermion number parity\nqnname_nf (default: \"Nf\"): name of total fermion number QN\nqnname_nfparity (default: \"NfParity\"): name of total fermion number parity QN","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"For example:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"sites = siteinds(\"Fermion\",N; conserve_nfparity=true)","category":"page"},{"location":"IncludedSiteTypes.html#\"Fermion\"-States","page":"SiteTypes Included with ITensor","title":"\"Fermion\" States","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"The available state names for \"Fermion\" sites are:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"0\" (aliases: \"Emp\") unoccupied fermion site\n\"1\" (aliases: \"Occ\") occupied fermion site","category":"page"},{"location":"IncludedSiteTypes.html#\"Fermion\"-Operators","page":"SiteTypes Included with ITensor","title":"\"Fermion\" Operators","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Operators associated with \"Fermion\" sites can be made using the op function, for example","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"C = op(\"C\",s)\nC4 = op(\"C\",sites[4])","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Single-fermion operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"N\" (aliases: \"n\") Density operator\n\"C\" (aliases: \"c\") Fermion annihilation operator\n\"Cdag\" (aliases: \"cdag\", \"c†\") Fermion creation operator\n\"F\" Jordan-Wigner string operator","category":"page"},{"location":"IncludedSiteTypes.html#\"Electron\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"Electron\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"The states of site indices with the \"Electron\" SiteType correspond to 0rangle, uparrowrangle, downarrowrangle, uparrowdownarrowrangle.","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Making a single \"Electron\" site or collection of N \"Electron\" sites","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"s = siteind(\"Electron\")\nsites = siteinds(\"Electron\",N)","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"conserve_qns (default: false): conserve total number of electrons\nconserve_sz (default: conserve_qns): conserve total S^z\nconserve_nf (default: conserve_qns): conserve total number of electrons\nconserve_nfparity (default: conserve_qns): conserve total electron number parity\nqnname_sz (default: \"Sz\"): name of total S^z QN\nqnname_nf (default: \"Nf\"): name of total electron number QN\nqnname_nfparity (default: \"NfParity\"): name of total electron number parity QN","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"For example:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"sites = siteinds(\"Electron\",N; conserve_nfparity=true)","category":"page"},{"location":"IncludedSiteTypes.html#\"Electron\"-States","page":"SiteTypes Included with ITensor","title":"\"Electron\" States","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"The available state names for \"Electron\" sites are:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Emp\" (aliases: \"0\") unoccupied electron site\n\"Up\" (aliases: \"↑\") electron site occupied with one up electron\n\"Dn\" (aliases: \"↓\") electron site occupied with one down electron\n\"UpDn\" (aliases: \"↑↓\") electron site occupied with two electrons (one up, one down)","category":"page"},{"location":"IncludedSiteTypes.html#\"Electron\"-Operators","page":"SiteTypes Included with ITensor","title":"\"Electron\" Operators","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Operators associated with \"Electron\" sites can be made using the op function, for example","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Cup = op(\"Cup\",s)\nCup4 = op(\"Cup\",sites[4])","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Single-fermion operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Ntot\" (aliases: \"ntot\") Total density operator\n\"Nup\" (aliases: \"n↑\") Up density operator\n\"Ndn\" (aliases: \"n↓\") Down density operator\n\"Cup\" (aliases: \"c↑\") Up-spin annihilation operator\n\"Cdn\" (aliases: \"c↓\") Down-spin annihilation operator\n\"Cdagup\" (aliases: \"c†↑\") Up-spin creation operator\n\"Cdagdn\" (aliases: \"c†↓\") Down-spin creation operator\n\"Sz\" (aliases: \"Sᶻ\")\n\"Sx\" (aliases: \"Sˣ\")\n\"S+\" (aliases: \"Sp\", \"S⁺\",\"Splus\")\n\"S-\" (aliases: \"Sm\", \"S⁻\", \"Sminus\")\n\"F\" Jordan-Wigner string operator\n\"Fup\" (aliases: \"F↑\") Up-spin Jordan-Wigner string operator\n\"Fdn\" (aliases: \"F↓\") Down-spin Jordan-Wigner string operator","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Non-fermionic single particle operators (these do not have Jordan-Wigner string attached, so will commute within systems such as OpSum or the apply function):","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Aup\" (aliases: \"a↑\") Up-spin annihilation operator\n\"Adn\" (aliases: \"a↓\") Down-spin annihilation operator\n\"Adagup\" (aliases: \"a†↑\") Up-spin creation operator\n\"Adagdn\" (aliases: \"a†↓\") Down-spin creation operator","category":"page"},{"location":"IncludedSiteTypes.html#\"tJ\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"tJ\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"tJ\" sites are similar to electron sites, but cannot be doubly occupied The states of site indices with the \"tJ\" SiteType correspond to 0rangle, uparrowrangle, downarrowrangle.","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Making a single \"tJ\" site or collection of N \"tJ\" sites","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"s = siteind(\"tJ\")\nsites = siteinds(\"tJ\",N)","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"conserve_qns (default: false): conserve total number of fermions\nconserve_nf (default: conserve_qns): conserve total number of fermions\nconserve_nfparity (default: conserve_qns): conserve total fermion number parity\nqnname_nf (default: \"Nf\"): name of total fermion number QN\nqnname_nfparity (default: \"NfParity\"): name of total fermion number parity QN","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"For example:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"sites = siteinds(\"tJ\",N; conserve_nfparity=true)","category":"page"},{"location":"IncludedSiteTypes.html#\"tJ\"-States","page":"SiteTypes Included with ITensor","title":"\"tJ\" States","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"The available state names for \"tJ\" sites are:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Emp\" (aliases: \"0\") unoccupied site\n\"Up\" (aliases: \"↑\") site occupied with one up electron\n\"Dn\" (aliases: \"↓\") site occupied with one down electron","category":"page"},{"location":"IncludedSiteTypes.html#\"tJ\"-Operators","page":"SiteTypes Included with ITensor","title":"\"tJ\" Operators","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Operators associated with \"tJ\" sites can be made using the op function, for example","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Cup = op(\"Cup\",s)\nCup4 = op(\"Cup\",sites[4])","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Single-fermion operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Ntot\" (aliases: \"ntot\") Total density operator\n\"Nup\" (aliases: \"n↑\") Up density operator\n\"Ndn\" (aliases: \"n↓\") Down density operator\n\"Cup\" (aliases: \"c↑\") Up-spin annihilation operator\n\"Cdn\" (aliases: \"c↓\") Down-spin annihilation operator\n\"Cdagup\" (aliases: \"c†↑\") Up-spin creation operator\n\"Cdagdn\" (aliases: \"c†↓\") Down-spin creation operator\n\"Sz\" (aliases: \"Sᶻ\")\n\"Sx\" (aliases: \"Sˣ\")\n\"S+\" (aliases: \"Sp\", \"S⁺\",\"Splus\")\n\"S-\" (aliases: \"Sm\", \"S⁻\", \"Sminus\")\n\"F\" Jordan-Wigner string operator\n\"Fup\" (aliases: \"F↑\") Up-spin Jordan-Wigner string operator\n\"Fdn\" (aliases: \"F↓\") Down-spin Jordan-Wigner string operator","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Non-fermionic single particle operators (these do not have Jordan-Wigner string attached, so will commute within systems such as OpSum or the apply function):","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Aup\" (aliases: \"a↑\") Up-spin annihilation operator\n\"Adn\" (aliases: \"a↓\") Down-spin annihilation operator\n\"Adagup\" (aliases: \"a†↑\") Up-spin creation operator\n\"Adagdn\" (aliases: \"a†↓\") Down-spin creation operator","category":"page"},{"location":"faq/HPC.html#High-Performance-Computing-(HPC)-Frequently-Asked-Questions","page":"High-Performance Computing FAQs","title":"High Performance Computing (HPC) Frequently Asked Questions","text":"","category":"section"},{"location":"faq/HPC.html#My-code-is-using-a-lot-of-RAM-what-can-I-do-about-this?","page":"High-Performance Computing FAQs","title":"My code is using a lot of RAM - what can I do about this?","text":"","category":"section"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"Tensor network algorithms can often use a large amount of RAM. On top of this essential fact, the Julia programming languge is \"garbage collected\" which means that unused memory isn't given back to the operating system right away, but only when the Julia runtime dynamically reclaims it. When your code allocates memory very rapidly, this can lead to high memory usage overall.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"Fortunately there are various steps you can take to keep the memory usage of your code under control.","category":"page"},{"location":"faq/HPC.html#.-Avoid-Repeatedly-Allocating,-Especially-in-Fast-or-\"Hot\"-Loops","page":"High-Performance Computing FAQs","title":"1. Avoid Repeatedly Allocating, Especially in Fast or \"Hot\" Loops","text":"","category":"section"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"More memory gets used whenever your code \"allocates\", which happens most commonly when you use dynamic storage types like Vector and Matrix. If you have a code pattern where you allocate or resize an array or vector inside a 'hot' loop, meaning a loop that iterates quickly very many times, the memory from the previous allocations may pile up very quickly before the next garbage collector run.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"To avoid this, allocate the array once before the loop begins if possible, then overwrite its contents during each iteration. More generally, try as much as possible to estimate the sizes of dynamic resources ahead of time. Or do one allocation that creates a large enough \"workspace\" that dynamic algorithms can reuse part of without reallocating the whole workspace (i.e. making a large array once then using portions of it when smaller arrays are needed).","category":"page"},{"location":"faq/HPC.html#.-Use-the-heap-size-hint-Flag","page":"High-Performance Computing FAQs","title":"2. Use the --heap-size-hint Flag","text":"","category":"section"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"A simple step you can take to help with overall memory usage is to pass the --heap-size-hint flag to the Julia program when you start it. For example, you can call Julia as:","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"julia --heap-size-hint=60G","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"When you pass this heap size, Julia will try to keep the memory usage at or below this value if possible.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"In cases where this does not work, your code simply may be allocating too much memory. Be sure not to allocate over and over again inside of \"hot\" loops which execute many times.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"Another possibility is that you are simply working with a tensor network with large bond dimensions, which may fundamentally use a lot of memory. In those cases, you can try to use features such as \"write to disk mode\" of the ITensor DMRG code or other related techniques. (See the write_when_maxdim_exceeds keyword of the ITensor dmrg function.)","category":"page"},{"location":"faq/HPC.html#.-In-Rare-Case,-Force-a-Garbage-Collection-Run","page":"High-Performance Computing FAQs","title":"3. In Rare Case, Force a Garbage Collection Run","text":"","category":"section"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"In some rare cases, such as when your code cannot be optimized to avoid any more allocations or when the --heap-size-hint provided above is not affecting the behavior of the Julia garbage collector, you can force the garbage collector (GC) to run at a specific point in your code by calling:","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"GC.gc()","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"Alternatively, you can call GC.gc(true) to force a \"full run\" rather than just collecting a more 'young' subset of previous allocations.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"While this approach works well to reduce memory usage, it can have the unfortunate downside of slowing down your code each time the garbage collector runs, which can be especially harmful to multithreaded or parallel algorithms. Therefore, if this approach must be used try calling GC.gc() as infrequently as possible and ideally only in the outermost functions and loops of your code (highest levels of your code).","category":"page"},{"location":"faq/HPC.html#Can-Julia-Be-Used-to-Perform-Parallel,-Distributed-Calculations-on-Large-Clusters?","page":"High-Performance Computing FAQs","title":"Can Julia Be Used to Perform Parallel, Distributed Calculations on Large Clusters?","text":"","category":"section"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"Yes. The Julia ecosystem offers multiple approaches to parallel computing across multiple machines including on large HPC clusters and including GPU resources.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"For an overall view of some of these options, the Julia on HPC Clusters website is a good resource.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"Some of the leading approaches to parallelism in Julia are:","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"MPI, through the MPI.jl package. Has the advantage of optionally using an MPI backend that is optimized for a particular cluster and possibly using fast interconnects like Infiniband.\nDagger, a framework for parallel computing across all kinds of resources, like CPUs and GPUs, and across multiple threads and multiple servers.\nDistributed. Part of the base Julia library, giving tools to perform calculations distributed across multiple machines.","category":"page"},{"location":"faq/HPC.html#Does-My-Cluster-Admin-Have-to-Install-Julia-for-Me?-What-are-the-Best-Practices-for-Installing-Julia-on-Clusters?","page":"High-Performance Computing FAQs","title":"Does My Cluster Admin Have to Install Julia for Me? What are the Best Practices for Installing Julia on Clusters?","text":"","category":"section"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"The most common approach to installing and using Julia on clusters is for users to install their own Julia binary and dependencies, which is quite easy to do. However, for certain libraries like MPI.jl, there may be MPI backends that are preferred by the cluster administrator. Fortunately, it is possible for admins to set global defaults for such backends and other library preferences.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"For more information on best practices for installing Julia on clusters, see the Julia on HPC Clusters website.","category":"page"},{"location":"faq/JuliaPkg.html#Julia-Package-Manager-Frequently-Asked-Questions","page":"Julia Package Manager FAQs","title":"Julia Package Manager Frequently Asked Questions","text":"","category":"section"},{"location":"faq/JuliaPkg.html#What-if-I-can't-upgrade-ITensors.jl-to-the-latest-version?","page":"Julia Package Manager FAQs","title":"What if I can't upgrade ITensors.jl to the latest version?","text":"","category":"section"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"Sometimes you may find that doing ] update ITensors or equivalently doing ] up ITensors within Julia package manager mode doesn't result in the ITensors package actually being upgraded. You may see that the current version you have remains stuck to a version that is lower than the latest one which you can check here.","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"What is most likely going on is that you have other packages installed which are blocking ITensors from being updated.","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"To get more information into which packages may be doing this, and what versions they are requiring, you can do the following. First look up the latest version of ITensors.jl. Let's say for this example that it is v0.3.0.","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"Next, input the following command while in package manager mode:","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"julia> ]\npkg> add ITensors@v0.3.0","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"If the package manager cannot update to this version, it will list all of the other packages that are blocking this from happening and give information about why. To go into a little more depth, each package has a compatibility or \"compat\" entry in its Project.toml file which says which versions of the ITensors package it is compatible with. If these versions do not include the latest one, perhaps because the package has not been updated, then it can block the ITensors package from being updated on your system.","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"Generally the solution is to just update each of these packages, then try again to update ITensors. If that does not work, then check the following","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"Are any of the blocking packages in \"dev mode\" meaning you called dev PackageName on them in the past? Try doing free PackageName if so to bring them out of dev mode.\nAre any of the blocking packages unregistered packages that were installed through a GitHub repo link? If so, you may need to do something like add https://github.com/Org/PackageName#main to force update that package to the latest code available on its main branch.","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"If you still can't get the ITensors package update, feel free to post a question or contact us for help.","category":"page"},{"location":"faq/QN.html#Quantum-Number-Frequently-Asked-Questions","page":"Quantum Number (QN) FAQs","title":"Quantum Number Frequently Asked Questions","text":"","category":"section"},{"location":"faq/QN.html#Can-I-mix-different-types-of-quantum-numbers-within-the-same-system?","page":"Quantum Number (QN) FAQs","title":"Can I mix different types of quantum numbers within the same system?","text":"","category":"section"},{"location":"faq/QN.html","page":"Quantum Number (QN) FAQs","title":"Quantum Number (QN) FAQs","text":"Yes, you can freely mix quantum numbers (QNs) of different types. For example, you can make the sites of your systems alternate between sites carrying spin \"Sz\" QNs and fermion sites carrying particle number \"Nf\" QNs. The QNs will not mix with each other and will separately be conserved to the original value you set for your initial wavefunction.","category":"page"},{"location":"faq/QN.html#How-can-I-separately-conserve-QNs-which-have-the-same-name?","page":"Quantum Number (QN) FAQs","title":"How can I separately conserve QNs which have the same name?","text":"","category":"section"},{"location":"faq/QN.html","page":"Quantum Number (QN) FAQs","title":"Quantum Number (QN) FAQs","text":"If you have two physically distinct types of sites, such as \"Qudit\" sites, but which carry identically named QNs called \"Number\", and you want the qudit number to be separately conserved within each type of site, you must make the QN names different for the two types of sites.","category":"page"},{"location":"faq/QN.html","page":"Quantum Number (QN) FAQs","title":"Quantum Number (QN) FAQs","text":"For example, the following line of code will make an array of site indices with the qudit number QN having the name \"Number_odd\" on odd sites and \"Number_even\" on even sites:","category":"page"},{"location":"faq/QN.html","page":"Quantum Number (QN) FAQs","title":"Quantum Number (QN) FAQs","text":"sites = [isodd(n) ? siteind(\"Qudit\", n; dim=10, conserve_qns=true, qnname_number=\"Number_odd\")\n : siteind(\"Qudit\", n; dim=2, conserve_qns=true, qnname_number=\"Number_even\")\n for n in 1:2*L]","category":"page"},{"location":"faq/QN.html","page":"Quantum Number (QN) FAQs","title":"Quantum Number (QN) FAQs","text":"(You may have to collapse the above code into a single line for it to run properly.)","category":"page"},{"location":"faq/RelationshipToOtherLibraries.html#Relationship-of-ITensor-to-other-tensor-libraries","page":"Relationship of ITensor to other tensor libraries FAQs","title":"Relationship of ITensor to other tensor libraries","text":"","category":"section"},{"location":"faq/RelationshipToOtherLibraries.html","page":"Relationship of ITensor to other tensor libraries FAQs","title":"Relationship of ITensor to other tensor libraries FAQs","text":"Here we will describe the relationship of ITensor to more traditional Julia Arrays or deep learning libraries like TensorFlow and PyTorch. There are a few things that distinguish ITensor from those approaches:","category":"page"},{"location":"faq/RelationshipToOtherLibraries.html","page":"Relationship of ITensor to other tensor libraries FAQs","title":"Relationship of ITensor to other tensor libraries FAQs","text":"ITensors have dimensions with labels that get passed around, which makes it simple to perform certain operations like contraction, addition, and tensor decompositions with a high level interface, independent of memory layout. This is along the same lines as Julia packages like NamedDims.jl and AxisArrays.jl and libraries in Python like xarray, however I would argue that the ITensor approach is a little more sophisticated (the dimensions have more metadata which makes them easier to manipulate for different situations, random ids to help avoid name clashes, etc.). This design was inspired by the needs of tensor network algorithms, where there are many tensor dimensions in the computation (of which many of them are dynamically created during the calculation), but would be helpful for writing other algorithms too.\nThe ITensor type has a dynamic high level interface, where the type itself is mutable and the data can be swapped out. This allows for conveniently allocating the data of an ITensor on the fly \"as needed\", which makes for a nicer, more flexible interface (like initializing an empty ITensor before a loop, and filling it with the correct data type when the first value is set), at the expense of a small overhead for accessing data in the ITensor. We have found this tradeoff is worth it, since we expect ITensors to be used for medium to large scale calculations where operations on the tensors like contraction, addition, and tensor decomposition dominate the cost of the calculation, and code can be designed with function barriers to speed up operations when data is being accessed repeatedly.\nAnother feature that ITensor has that goes beyond what is available in standard Julia, TensorFlow, and PyTorch is tensors which are symmetric under a group action. The physical interpretation of these tensors are ones that have a conserved quantity (like a quantum state with a conserved number of particles), so that feature is more physics-oriented, but could have applications in other areas like machine learning as well. In practice, these tensors are block sparse, and have extra metadata on the dimensions labeling representations of the group.\nBased on the features above, the ITensor library provides high level implementations of tensor network algorithms (algebraic operations of very high dimensional tensors, such as addition, multiplication, and finding dominant eigenvectors). In general these algorithms can (and have been) written on top of other libraries like standard Julia Arrays/AD, PyTorch, or TensorFlow, but they might have various downsides (a less convenient interface for dealing with tensor operations, no support for the types of symmetric tensors we often need, limited support for tensors with complex numbers in the case of libraries like PyTorch, though perhaps that has improved since I last checked, etc.).","category":"page"},{"location":"faq/RelationshipToOtherLibraries.html","page":"Relationship of ITensor to other tensor libraries FAQs","title":"Relationship of ITensor to other tensor libraries FAQs","text":"Although ITensor has primarily focused on quantum physics and quantum computing applications, there is work using ITensor for machine learning applications (so far focused on applications of tensor networks to machine learning, so no neural network calculations yet as far as I know). In general, these different libraries (ITensor, Flux, PyTorch, TensorFlow) are biased towards their specific methods and application areas that they are used for the most: ITensor is more biased towards tensor network calculations and quantum physics/quantum computing applications, based on the available features and interface, while PyTorch and TensorFlow are more biased towards neural network calculations. However, our goal would be to provide more features to ITensor that would make it useful for neural network applications as well, such as better support for slicing operations.","category":"page"},{"location":"AdvancedUsageGuide.html#advanced_usage_guide","page":"Advanced Usage Guide","title":"Advanced ITensor Usage Guide","text":"","category":"section"},{"location":"AdvancedUsageGuide.html#Installing-and-updating-ITensors.jl","page":"Advanced Usage Guide","title":"Installing and updating ITensors.jl","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"The ITensors package can be installed with the Julia package manager. Assuming you have already downloaded Julia, which you can get here, from the Julia REPL, type ] to enter the Pkg REPL mode and run:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"$ julia","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> ]\n\npkg> add ITensors","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Or, equivalently, via the Pkg API:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> import Pkg; Pkg.add(\"ITensors\")","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"We recommend using ITensors.jl with Intel MKL in order to get the best possible performance. If you have not done so already, you can replace the current BLAS and LAPACK implementation used by Julia with MKL by using the MKL.jl package. Please follow the instructions here.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"To use the latest registered (stable) version of ITensors.jl, use update ITensors in Pkg mode or import Pkg; Pkg.update(\"ITensors\"). We will commonly release new patch versions (such as updating from v0.1.12 to v0.1.13) with bug fixes and improvements. However, make sure to double check before updating between minor versions (such as from v0.1.41 to v0.2.0) because new minor releases may be breaking.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Remember that if you are compiling system images of ITensors.jl, such as with the ITensors.compile() command, you will need to rerurn this command to compile the new version of ITensor after an update.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"To try the \"development branch\" of ITensors.jl (for example, if there is a feature or fix we added that hasn't been released yet), you can do add ITensors#main. You can switch back to the latest released version with add ITensors. Using the development/main branch is generally not encouraged unless you know what you are doing.","category":"page"},{"location":"AdvancedUsageGuide.html#Using-ITensors.jl-in-the-REPL","page":"Advanced Usage Guide","title":"Using ITensors.jl in the REPL","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"There are many ways you can write code based on ITensors.jl, ranging from using it in the REPL to writing a small script to making a package that depends on it.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"For example, you can just start the REPL from your command line like:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"$ julia","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"assuming you have an available version of Julia with the ITensors.jl package installed. Then just type:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using ITensors","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"and start typing ITensor commands. For example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> i = Index(2, \"i\")\n(dim=2|id=355|\"i\")\n\njulia> A = randomITensor(i, i')\nITensor ord=2 (dim=2|id=355|\"i\") (dim=2|id=355|\"i\")'\nNDTensors.Dense{Float64,Array{Float64,1}}\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=355|\"i\")\nDim 2: (dim=2|id=355|\"i\")'\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 1.2320011464276275 1.8504245734277216\n 1.0763652402177477 0.030353720156277037\n\njulia> (A*dag(A))[]\n3.9627443142240617","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Note that there are some \"gotchas\" with working in the REPL like this. Technically, all commands in the REPL are in the \"global scope\". The global scope might not work as you would expect, for example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> for _ in 1:3\n A *= 2\n end\nERROR: UndefVarError: A not defined\nStacktrace:\n [1] top-level scope at ./REPL[12]:2\n [2] eval(::Module, ::Any) at ./boot.jl:331\n [3] eval_user_input(::Any, ::REPL.REPLBackend) at /home/mfishman/software/julia-1.4.0/share/julia/stdlib/v1.4/REPL/src/REPL.jl:86\n [4] run_backend(::REPL.REPLBackend) at /home/mfishman/.julia/packages/Revise/AMRie/src/Revise.jl:1023\n [5] top-level scope at none:0","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"since the A inside the for-loop introduces a new local variable. Some alternatives are to wrap that part of the code in a let-block or a function:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> function f(A)\n for _ in 1:3\n A *= 2\n end\n A\n end\nf (generic function with 1 method)\n\njulia> A = f(A)\nITensor ord=2 (dim=2|id=355|\"i\") (dim=2|id=355|\"i\")'\nNDTensors.Dense{Float64,Array{Float64,1}}\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=355|\"i\")\nDim 2: (dim=2|id=355|\"i\")'\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 9.85600917142102 14.803396587421773\n 8.610921921741982 0.2428297612502163","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"In this particular case, you can alternatively modify the ITensor in-place:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> for _ in 1:3\n A ./= 2\n end\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=355|\"i\")\nDim 2: (dim=2|id=355|\"i\")'\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 1.2320011464276275 1.8504245734277216\n 1.0763652402177477 0.030353720156277037","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"A common place you might accidentally come across this is when you are creating a Hamiltonian with OpSum:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> N = 4;\n\njulia> sites = siteinds(\"S=1/2\",N);\n\njulia> os = OpSum();\n\njulia> for j=1:N-1\n os += \"Sz\", j, \"Sz\", j+1\n end\nERROR: UndefVarError: os not defined\nStacktrace:\n [1] top-level scope at ./REPL[16]:2\n [2] eval(::Module, ::Any) at ./boot.jl:331\n [3] eval_user_input(::Any, ::REPL.REPLBackend) at /home/mfishman/software/julia-1.4.0/share/julia/stdlib/v1.4/REPL/src/REPL.jl:86\n [4] run_backend(::REPL.REPLBackend) at /home/mfishman/.julia/packages/Revise/AMRie/src/Revise.jl:1023\n [5] top-level scope at none:0","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"In this case, you can use os .+= (\"Sz\", j, \"Sz\", j+1), add!(os, \"Sz\", j, \"Sz\", j+1), or wrap your code in a let-block or function.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Take a look at Julia's documentation here for rules on scoping. Also note that this behavior is particular to Julia v1.4 and below, and is expected to change in v1.5.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Note that the REPL is very useful for prototyping code quickly, but working directly in the REPL and outside of functions can cause sub-optimal performance. See Julia's performance tips for more information.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"We recommend the package OhMyREPL which adds syntax highlighting to the Julia REPL.","category":"page"},{"location":"AdvancedUsageGuide.html#Finding-documentation-interactively","page":"Advanced Usage Guide","title":"Finding documentation interactively","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Julia provides many tools for searching for documentation interactively at the REPL. Say that you want to learn more about how to use an ITensor from the command line. You can start by typing ? followed by ITensor:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using ITensors\n\njulia> ?ITensor\nsearch: ITensor ITensors itensor emptyITensor randomITensor\n\n An ITensor is a tensor whose interface is independent of its\n memory layout. Therefore it is not necessary to know the ordering\n of an ITensor's indices, only which indices an ITensor has.\n Operations like contraction and addition of ITensors automatically\n handle any memory permutations.\n\n Examples\n ≡≡≡≡≡≡≡≡≡≡\n\n julia> i = Index(2, \"i\")\n (dim=2|id=287|\"i\")\n\n julia> A = randomITensor(i', i)\n ITensor ord=2 (dim=2|id=287|\"i\")' (dim=2|id=287|\"i\")\n NDTensors.Dense{Float64,Array{Float64,1}}\n\n julia> @show A;\n A = ITensor ord=2\n Dim 1: (dim=2|id=287|\"i\")'\n Dim 2: (dim=2|id=287|\"i\")\n NDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 0.28358594718392427 1.4342219756446355\n 1.6620103556283987 -0.40952231269251566\n\n julia> @show inds(A);\n inds(A) = IndexSet{2} (dim=2|id=287|\"i\")' (dim=2|id=287|\"i\")\n[...]","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"(the specific output may be different for different versions of ITensors.jl as we update the docs). You can use the help prompt (which you get by typing ? at the REPL) to print out documentation for types and methods.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Another way to get information about types is with the function fieldnames:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> fieldnames(ITensor)\n(:store, :inds)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"which shows the fields of a type. Note that in general the specific names of the fields and structures of types may change (we consider those to be internal details), however we often make functions to access the fields of a type that have the same name as the field, so it is a good place to get started. For example, you can access the storage and indices of an ITensor A with the functions store(A) and inds(A).","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Another helpful function is apropos, which search through all documentation for a string (ignoring the case) and prints a list of all types and methods with documentation that contain the string. For example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> apropos(\"IndexSet\")\nITensors.IndexSet\nITensors.push\nITensors.insertat\nITensors.getfirst\nITensors.commoninds\nITensors.pushfirst\nNDTensors.mindim\n[...]","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This can often return too much information. A helpful way to narrow down the search is with regular expressions, for example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> apropos(r\"ITensor.*IndexSet\")\nITensors.block\nITensors.hasinds\nITensors.ITensor\nNDTensors.inds","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"where the notation r\"...\" is Julia notation for making a string that will be interpreted as a regular expression. Here, we are searching for any documentation that contains the string \"ITensor\" followed at some point by \"IndexSet\". The notation .* is regular expression notation for matching any number of any type of character.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Based on the apropos function, we can make some helper functions that may be useful. For example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"using ITensors\n\nfunction finddocs(s)\n io = IOBuffer()\n apropos(io, s)\n v = chomp(String(take!(io)))\n return split(v, \"\\n\")\nend\n\nfunction finddocs(s...)\n intersect(finddocs.(s)...)\nend\n\nfound_methods = finddocs(\"indices\", \"set difference\")\ndisplay(found_methods)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"returns:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"3-element Array{SubString{String},1}:\n \"ITensors.noncommoninds\"\n \"Base.setdiff\"\n \"ITensors.uniqueinds\"","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"which are the functions that have docs that contain the strings \"indices\" and \"set difference\". We can print the docs for uniqueinds to find:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"help?> uniqueinds\nsearch: uniqueinds unique_siteinds uniqueind uniqueindex\n\n uniqueinds(A, B; kwargs...)\n uniqueinds(::Order{N}, A, B; kwargs...)\n\n\n Return an IndexSet with indices that are unique to the set of\n indices of A and not in B (the set difference).\n\n Optionally, specify the desired number of indices as Order(N),\n which adds a check and can be a bit more efficient.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"We can also filter the results to only specify functions from certain modules, for example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> filter(x -> startswith(x, \"ITensors\"), finddocs(\"indices\", \"set difference\"))\n2-element Array{SubString{String},1}:\n \"ITensors.noncommoninds\"\n \"ITensors.uniqueinds\"\n\njulia> filter(x -> !startswith(x, \"ITensors\"), finddocs(\"indices\", \"set difference\"))\n1-element Array{SubString{String},1}:\n \"Base.setdiff\"","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Ideally we could have apropos do a \"smart\" Google-like search of the appropriate docstrings, but this is a pretty good start.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Additionally, the names function can be useful, which prints the names of all functions and types that are exported by a module. For example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> names(ITensors)\n264-element Array{Symbol,1}:\n Symbol(\"@OpName_str\")\n Symbol(\"@SiteType_str\")\n Symbol(\"@StateName_str\")\n Symbol(\"@TagType_str\")\n Symbol(\"@disable_warn_order\")\n Symbol(\"@reset_warn_order\")\n Symbol(\"@set_warn_order\")\n Symbol(\"@ts_str\")\n :AbstractObserver\n :OpSum\n :DMRGObserver\n :ITensor\n :ITensors\n :Index\n[...]","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Of course this is a very long list (and the methods are returned as Symbols, which are like strings but not as easy to work with). However, we can convert the list to strings and filter the strings to find functions we are interested in, for example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> filter(x -> contains(x, \"common\") && contains(x, \"ind\"), String.(names(ITensors)))\n8-element Array{String,1}:\n \"common_siteind\"\n \"common_siteinds\"\n \"commonind\"\n \"commonindex\"\n \"commoninds\"\n \"hascommoninds\"\n \"noncommonind\"\n \"noncommoninds\"","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Julia types do not have member functions, so people coming from object oriented programming languages may find that at first it is more difficult to find methods that are applicable to a certain type. However, Julia has many fantastic tools for introspection that we can use to make this task easier.","category":"page"},{"location":"AdvancedUsageGuide.html#Make-a-small-project-based-on-ITensors.jl","page":"Advanced Usage Guide","title":"Make a small project based on ITensors.jl","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Once you start to have longer code, you will want to put your code into one or more files. For example, you may have a short script with one or more functions based on ITensors.jl:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"# my_itensor_script.jl\nusing ITensors\n\nfunction norm2(A::ITensor)\n return (A*dag(A))[]\nend","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Then, in the same directory as your script my_itensor_script.jl, just type:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> include(\"my_itensor_script.jl\");\n\njulia> i = Index(2; tags=\"i\");\n\njulia> A = randomITensor(i', i);\n\njulia> norm2(A)\n[...]","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"As your code gets longer, you can split it into multiple files and include this files into one main project file, for example if you have two files with functions in them:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"# file1.jl\n\nfunction norm2(A::ITensor)\n return (A*dag(A))[]\nend","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"and","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"# file2.jl\n\nfunction square(A::ITensor)\n return A .^ 2\nend","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"# my_itensor_project.jl\n\nusing ITensors\n\ninclude(\"file1.jl\")\n\ninclude(\"file2.jl\")","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Then, as before, you can use your functions at the Julia REPL by just including the file my_itensor_project.jl:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> include(\"my_itensor_project.jl\");\n\njulia> i = Index(2; tags=\"i\");\n\njulia> A = randomITensor(i', i);\n\njulia> norm2(A)\n[...]\n\njulia> square(A)\n[...]","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"As your code gets more complicated and has more files, it is helpful to organize it into a package. That will be covered in the next section.","category":"page"},{"location":"AdvancedUsageGuide.html#Make-a-Julia-package-based-on-ITensors.jl","page":"Advanced Usage Guide","title":"Make a Julia package based on ITensors.jl","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"In this section, we will describe how to make a Julia package based on ITensors.jl. This is useful to do when your project gets longer, since it helps with:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Code organization.\nAdding dependencies that will get automatically installed through Julia's package system.\nVersioning.\nAutomated testing.\nCode sharing and easier package installation.\nOfficially registering your package with Julia.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"and many more features that we will mention later.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Start up Julia and install PkgTemplates","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"$ julia\n\njulia> ]\n\npkg> add PkgTemplates","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"then press backspace and type:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using PkgTemplates\n\njulia> t = Template(; user=\"your_github_username\", plugins=[Git(; ssh=true),])\n\njulia> t(\"MyITensorsPkg\")","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"You should put your Github account name instead of \"your_github_username\", if you want to use Github to host your package. The option plugins=[Git(; ssh=true),] sets the Github authentication to use ssh, which is generally more convenient. You can switch to https (where you have to type your username and password to push changes) by setting ssh=false or leaving off plugins=[...]. By default, the package will be located in the directory ~/.julia/dev, you can change this with the keyword argument dir=[...]. However, ~/.julia/dev is recommended since that is the directory Julia's package manager (and other packages like Revise) will look for development packages. Please see the PkgTemplate documentation for more customization options.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Then, we want to tell Julia about our new package. We do this as follows:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> ]\n\npkg> dev ~/.julia/dev/MyITensorsPkg","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"then you can do:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using MyITensorsPkg","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"from any directory to use your new package. However, it doesn't have any functions available yet. Additionally, there should be an empty test file already set up here:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"~/.julia/dev/MyITensorsPkg/test/runtests.jl","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"which you can run from any directory like:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> ]\n\npkg> test MyITensorsPkg","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"It should show something like:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"[...]\nTest Summary: |\nMyITensorsPkg.jl | No tests\n Testing MyITensorsPkg tests passed","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"since there are no tests yet.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"First we want to add ITensors as a dependency of our package. We do this by \"activating\" our package environment and then adding ITensors:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> ]\n\npkg> activate MyITensorsPkg\n\n(MyITensorsPkg) pkg> add ITensors","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This will edit the file ~/.julia/dev/MyITensorsPkg/Project.toml and add the line","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"[deps]\nITensors = \"9136182c-28ba-11e9-034c-db9fb085ebd5\"","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Because your package is under development, back in the main Pkg environment you should type resolve:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"(MyITensorsPkg) pkg> activate\n\npkg> resolve","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Now, if you or someone else uses the package, it will automatically install ITensors.jl for you.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Now your package is set up to develop! Try editing the file ~/.julia/dev/MyITensorsPkg/src/MyITensorsPkg.jl and add the norm2 function, which calculates the squared norm of an ITensor:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"module MyITensorsPkg\n\nusing ITensors\n\nexport norm2\n\nnorm2(A::ITensor) = (A*dag(A))[]\n\nend","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"The export command makes norm2 available in the namespace without needing to type MyITensorsPkg.norm2 when you do using MyITensorsPkg. Now in a new Julia session you can do:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using ITensors\n\njulia> i = Index(2)\n(dim=2|id=263)\n\njulia> A = randomITensor(i)\nITensor ord=1 (dim=2|id=263)\nNDTensors.Dense{Float64,Array{Float64,1}}\n\njulia> norm(A)^2\n6.884457016011188\n\njulia> norm2(A)\nERROR: UndefVarError: norm2 not defined\n[...]\n\njulia> using MyITensorsPkg\n\njulia> norm2(A)\n6.884457016011188","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Unfortunately, if you continue to edit the file MyITensorsPkg.jl, even if you type using MyITensorsPkg again, if you are in the same Julia session the changes will not be reflected, and you will have to restart your Julia session. The Revise package will allow you to edit your package files and have the changes reflected in real time in your current Julia session, so you don't have to restart the session.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Now, we can add some tests for our new functionality. Edit the file ~/.julia/dev/MyITensorsPkg/test/runtests.jl to look like:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"using MyITensorsPkg\nusing ITensors\nusing Test\n\n@testset \"MyITensorsPkg.jl\" begin\n i = Index(2)\n A = randomITensor(i)\n @test isapprox(norm2(A), norm(A)^2)\nend","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Now when you test your package you should see:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"pkg> test MyITensorsPkg\n[...]\nTest Summary: | Pass Total\nMyITensorsPkg.jl | 1 1\n Testing MyITensorsPkg tests passed","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Your package should already be set up as a git repository by the PkgTemplates commands we started with. We recommend using Github or similar versions control systems for your packages, especially if you plan to make them public and officially register them as Julia packages.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"You can set up your local package as a Github repository by following the steps here. Many of the steps may be unnecessary since they were already set up by PkgTemplates. You should be able to go to the website here, create a new Github repository with the name MyITensorsPkg.jl, and then following the instructions under \"push an existing repository from the command line\".","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"You may also want to switch between HTTPS and SSH authentication as described here, if you didn't choose your preferred authentication protocol with PkgTemplates.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"There are many more features you can add to your package through various Julia packages and Github, for example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Control of precompilation with tools like SnoopCompile.\nAutomatic testing of your package at every pull request/commit with Github Actions, Travis, or similar services.\nAutomated benchmarking of your package at every pull request with BenchmarkTools, PkgBenchmark and BenchmarkCI.\nAutomated building of your documentation with Documenter.\nCompiling your package with PackageCompiler.\nAutomatically check what parts of your code your tests check with code coverage.\nOfficially register your Julia package so that others can easily install it and follow along with updated versions using the Registrator.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"You can take a look at the ITensors Github page for inspiration on setting up some of these services and ideas for organizing your package.","category":"page"},{"location":"AdvancedUsageGuide.html#Developing-ITensors.jl","page":"Advanced Usage Guide","title":"Developing ITensors.jl","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This section is for someone who is interested in modifying the source code of ITensors.jl, and then possibly contribute you changes to the official ITensors.jl package.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This should not be necessary for most people. If for whatever reason you think that the functionality of ITensors.jl needs to be modified, oftentimes you can add new functions outside of ITensors.jl or directly overload a function of ITensors.jl (for example with the import keyword).","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"However, if you would like to only modify parts of the internals of an ITensors.jl function, and/or plan to contribute changes like bug fixes or new features to the official ITensors.jl package, this section is for you.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"If you install a package like ITensors with the package manager using the standard Pkg.add command:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using Pkg\n\njulia> Pkg.add(\"ITensors\")","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"it will automatically clone the latest registered/tagged version of ITensors in a randomly generated directory inside ~/.julia/packages. You can find out what version you are using with Pkg.status:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> Pkg.status(\"ITensors\")\n Status `~/.julia/environments/v1.7/Project.toml`\n [9136182c] ITensors v0.2.16","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"and you can use pkgdir to find out the directory of the source code of a package that you have loaded:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using ITensors\n\njulia> pkgdir(ITensors)\n\"/home/mfishman/.julia/packages/ITensors/cu9Bo\"","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"The source code of a package loaded in this way is read-only, so you won't be able to modify it.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"If you want to modify the source code of ITensors.jl, you should check out the packages NDTensors.jl and ITensors.jl in development mode with Pkg.develop:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> Pkg.develop([\"NDTensors\", \"ITensors\"])\nPath `/home/mfishman/.julia/dev/ITensors` exists and looks like the correct repo. Using existing path.\n Resolving package versions...\n Updating `~/.julia/environments/v1.7/Project.toml`\n [9136182c] ~ ITensors v0.2.16 ⇒ v0.2.16 `~/.julia/dev/ITensors`\n [23ae76d9] ~ NDTensors v0.1.35 ⇒ v0.1.35 `~/.julia/dev/ITensors/NDTensors`\n Updating `~/.julia/environments/v1.7/Manifest.toml`\n [9136182c] ~ ITensors v0.2.16 ⇒ v0.2.16 `~/.julia/dev/ITensors`\n [23ae76d9] ~ NDTensors v0.1.35 ⇒ v0.1.35 `~/.julia/dev/ITensors/NDTensors`\n\njulia> Pkg.status([\"NDTensors\", \"ITensors\"])\n Status `~/.julia/environments/v1.7/Project.toml`\n [9136182c] ITensors v0.2.16 `~/.julia/dev/ITensors`\n [23ae76d9] NDTensors v0.1.35 `~/.julia/dev/ITensors/NDTensors`","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Then, Julia will use the version of ITensors.jl living in the directory ~/.julia/dev/ITensors and the version of NDTensors.jl living in the directory ~/.julia/dev/ITensors/NDTensors, though you may need to restart Julia for this to take affect.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"We recommend checking out the development versions of both NDTensors.jl and ITensors.jl since we often develop both packages tandem, so the development branch of ITensors.jl may rely on changes we make in NDTensors.jl.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"By default, when you modify code in ~/.julia/dev/ITensors or ~/.julia/dev/ITensors/NDTensors you will need to restart Julia for the changes to take affect. A way around this issue is the Revise package. We highly recommend using the Revise package when you are developing packages, which automatically detects changes you are making to a package you have checked out for development and edit code and not have to restart your Julia session. In short, if you have Revise.jl loaded, you can edit the code in ~/.julia/dev/ITensors or ~/.julia/dev/ITensors/NDTensors and the changes you make will be reflected on the fly as you use the package (there are some limitations, for example you will need to restart Julia if you change the definitions of types).","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Note that the code in ~/.julia/dev/ITensors is just a git repository cloned from the repository https://github.com/ITensor/ITensors.jl, so you can do anything that you would with any other git repository (use forks of the project, check out branches, push and pull changes, etc.).","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"The standard procedure for submitting a bug fix or new feature to ITensors.jl would then be to first fork the ITensors.jl repository. Then, check out your fork for development with:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using Pkg\n\njulia> Pkg.develop(url=\"https://github.com/mtfishman/ITensors.jl\")","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"where you would replace mtfishman with your own Github username. Make the changes to the code in ~/.julia/dev/ITensors, push the changes to your fork, and then make a pull request to the ITensors.jl Github repository.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"To go back to the official version of the NDTensors.jl and ITensors.jl packages, you can use the command Pkg.free([\"NDTensors\", \"ITensors\"]):","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> Pkg.free([\"NDTensors\", \"ITensors\"])\n Resolving package versions...\n Updating `~/.julia/environments/v1.7/Project.toml`\n [9136182c] ~ ITensors v0.2.16 `~/.julia/dev/ITensors` ⇒ v0.2.16\n [23ae76d9] ~ NDTensors v0.1.35 `~/.julia/dev/ITensors/NDTensors` ⇒ v0.1.35\n Updating `~/.julia/environments/v1.7/Manifest.toml`\n [9136182c] ~ ITensors v0.2.16 `~/.julia/dev/ITensors` ⇒ v0.2.16\n [23ae76d9] ~ NDTensors v0.1.35 `~/.julia/dev/ITensors/NDTensors` ⇒ v0.1.35\n\njulia> Pkg.status([\"NDTensors\", \"ITensors\"])\n Status `~/.julia/environments/v1.7/Project.toml`\n [9136182c] ITensors v0.2.16\n [23ae76d9] NDTensors v0.1.35","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"so it returns to the version of the package you would have just after installing with Pkg.add.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Some of the Julia package development workflow definitely takes some getting used to, but once you figure out the \"flow\" and have a picture of what is going on there are only a small set of commands you really need to use.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"A small note is that we follow the Blue style guide for formatting the source code in ITensors.jl. To make this more automated, we use the wonderful package JuliaFormatter.jl. To format your developed version of ITensors.jl, all you have to do is change your directory to ~/.julia/dev/ITensors and run the command format(\".\") after loading the JuliaFormatter package:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using Pkg\n\njulia> Pkg.status(\"ITensors\")\n Status `~/.julia/environments/v1.7/Project.toml`\n [9136182c] ITensors v0.2.16 `~/.julia/dev/ITensors`\n\njulia> using ITensors\n\njulia> pkgdir(ITensors)\n\"/home/mfishman/.julia/dev/ITensors\"\n\njulia> cd(pkgdir(ITensors))\n\njulia> using JuliaFormatter\n\njulia> format(\".\")\nfalse\n\njulia> format(\".\") # Check the formatting succeeded\ntrue","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This will automatically change the style of the code according to the Blue style guide. The format command returns false if the code was not already formatted (and therefore if the command made changes to the source code to follow the style guide), and returns true otherwise.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"If you make changes to ITensors that you think will be useful to others, such as fixing bugs or adding new features, please consider making a pull request. However, please ask us first before doing so – either by raising an issue on Github or asking a question on the ITensor support forum – to make sure it is a change or addition that we will want to include or to check that it is not something we are currently working on. Coordinating with us in that way will help save your time and energy as well as ours!","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Here is a great introduction to Julia package development as well as making pull requests to existing Julia packages by the irreplacable Chris Rackauckas.","category":"page"},{"location":"AdvancedUsageGuide.html#Compiling-ITensors.jl","page":"Advanced Usage Guide","title":"Compiling ITensors.jl","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"You might notice that the time to load ITensors.jl (with using ITensors) and the time to run your first few ITensor commands is slow. This is due to Julia's just-in-time (JIT) compilation. Julia is compiling special versions of each function that is being called based on the inputs that it gets at runtime. This allows it to have fast code, often nearly as fast as fully compiled languages like C++, while still being a dynamic language.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"However, the long startup time can still be annoying. In this section, we will discuss some strategies that can be used to minimize this annoyance, for example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Precompilation.\nStaying in the same Julia session with Revise.\nUsing PackageCompiler to compile ITensors.jl ahead of time.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Precompilation is performed automatically when you first install ITensors.jl or update a version and run the command using ITensors for the first time. For example, when you first use ITensors after installation or updating, you will see:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using ITensors\n[ Info: Precompiling ITensors [9136182c-28ba-11e9-034c-db9fb085ebd5]","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"The process is done automatically, and puts some compiled binaries in your ~/.julia directory. The goal is to decrease the time it takes when you first type using ITensors in your next Julia session, and also the time it takes for you to first run ITensor functions in a new Julia session. This helps the startup time, but currently doesn't help enough. This is something both ITensors.jl and the Julia language will try to improve over time.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"To avoid this time, it is recommended that you work as much as you can in a single Julia session. You should not need to restart your Julia session very often. For example, if you are writing code in a script, just include the file again which will pull in the new changes to the script (the exception is if you change the definition of a type you made, which would requiring restarting the REPL).","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"If you are working on a project, we highly recommend using the Revise package which automatically detects changes you are making in your packages and reflects them real-time in your current REPL session. Using these strategies should minimize the number of times you need to restart your REPL session.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"If you plan to use ITensors.jl directly from the command line (i.e. not from the REPL), and the startup time is an issue, you can try compiling ITensors.jl using PackageCompiler.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Before using PackageCompiler to compile ITensors, when we first start using ITensors.jl we might see:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> @time using ITensors\n 3.845253 seconds (10.96 M allocations: 618.071 MiB, 3.95% gc time)\n\njulia> @time i = Index(2);\n 0.000684 seconds (23 allocations: 20.328 KiB)\n\njulia> @time A = randomITensor(i', i);\n 0.071022 seconds (183.24 k allocations: 9.715 MiB)\n\njulia> @time svd(A, i');\n 5.802053 seconds (24.56 M allocations: 1.200 GiB, 7.83% gc time)\n\njulia> @time svd(A, i');\n 0.000177 seconds (450 allocations: 36.609 KiB)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"ITensors provides the command ITensors.compile() to create what is called a \"custom system image\", a custom version of Julia that includes a compiled version of ITensors (see the PackageCompiler documentation for more details). Just run the command:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> ITensors.compile()\n[...]","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"By default, this will create the file sys_itensors.so in the directory ~/.julia/sysimages. Then if we start julia with:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"$ julia --sysimage ~/.julia/sysimages/sys_itensors.so","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"then you should see something like:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> @time using ITensors\n 0.330587 seconds (977.61 k allocations: 45.807 MiB, 1.89% gc time)\n\njulia> @time i = Index(2);\n 0.000656 seconds (23 allocations: 20.328 KiB)\n\njulia> @time A = randomITensor(i', i);\n 0.000007 seconds (7 allocations: 576 bytes)\n\njulia> @time svd(A, i');\n 0.263526 seconds (290.02 k allocations: 14.220 MiB)\n\njulia> @time svd(A, i');\n 0.000135 seconds (350 allocations: 29.984 KiB)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"which is much better.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Note that you will have to recompile ITensors with the command ITensors.compile() any time that you update the version of ITensors in order to keep the system image updated. We hope to make this process more automated in the future.","category":"page"},{"location":"AdvancedUsageGuide.html#Benchmarking-and-profiling","page":"Advanced Usage Guide","title":"Benchmarking and profiling","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Julia has great built-in tools for benchmarking and profiling. For benchmarking fast code at the command line, you can use BenchmarkTools:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using ITensors;\n\njulia> using BenchmarkTools;\n\njulia> i = Index(100, \"i\");\n\njulia> A = randomITensor(i, i');\n\njulia> @btime 2*$A;\n 4.279 μs (8 allocations: 78.73 KiB)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"We recommend packages like ProfileView to get detailed profiles of your code, in order to pinpoint functions or lines of code that are slower than they should be.","category":"page"},{"location":"AdvancedUsageGuide.html#ITensor-type-design-and-writing-performant-code","page":"Advanced Usage Guide","title":"ITensor type design and writing performant code","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Advanced users might notice something strange about the definition of the ITensor type, that it is often not \"type stable\". Some of this is by design. The definition for ITensor is:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"mutable struct ITensor\n inds::IndexSet\n store::TensorStorage\nend","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"These are both abstract types, which is something that is generally discouraged for peformance.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This has a few disadvantages. Some code that you might expect to be type stable, like getindex, is not, for example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> i = Index(2, \"i\");\n\njulia> A = randomITensor(i, i');\n\njulia> @code_warntype A[i=>1, i'=>2]\nVariables\n #self#::Core.Compiler.Const(getindex, false)\n T::ITensor\n ivs::Tuple{Pair{Index{Int64},Int64}}\n p::Tuple{Union{Nothing, Int64}}\n vals::Tuple{Any}\n\nBody::Number\n1 ─ %1 = NDTensors.getperm::Core.Compiler.Const(NDTensors.getperm, false)\n│ %2 = ITensors.inds(T)::IndexSet{1,IndexT,DataT} where DataT<:Tuple where IndexT<:Index\n│ %3 = Base.broadcasted(ITensors.ind, ivs)::Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple},Nothing,typeof(ind),Tuple{Tuple{Pair{Index{Int64},Int64}}}}\n│ %4 = Base.materialize(%3)::Tuple{Index{Int64}}\n│ (p = (%1)(%2, %4))\n│ %6 = NDTensors.permute::Core.Compiler.Const(NDTensors.permute, false)\n│ %7 = Base.broadcasted(ITensors.val, ivs)::Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple},Nothing,typeof(val),Tuple{Tuple{Pair{Index{Int64},Int64}}}}\n│ %8 = Base.materialize(%7)::Tuple{Int64}\n│ (vals = (%6)(%8, p))\n│ %10 = Core.tuple(T)::Tuple{ITensor}\n│ %11 = Core._apply_iterate(Base.iterate, Base.getindex, %10, vals)::Number\n│ %12 = Core.typeassert(%11, ITensors.Number)::Number\n└── return %12\n\njulia> typeof(A[i=>1, i'=>2])\nFloat64","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Uh oh, that doesn't look good! Julia can't know ahead of time, based on the inputs, what the type of the output is, besides that it will be a Number (though at runtime, the output has a concrete type, Float64).","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"So why is it designed this way? The main reason is to allow more generic and dynamic code than traditional, statically-typed Arrays. This allows us to have code like:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> i = Index(2, \"i\")\n(dim=2|id=811|\"i\")\n\njulia> A = emptyITensor(i', i);\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=811|\"i\")'\nDim 2: (dim=2|id=811|\"i\")\nNDTensors.Empty{Float64,NDTensors.Dense{Float64,Array{Float64,1}}}\n 2×2\n\n\n\njulia> A[i' => 1, i => 2] = 1.2;\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=811|\"i\")'\nDim 2: (dim=2|id=811|\"i\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 0.0 1.2\n 0.0 0.0","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Here, the type of the storage of A is changed in-place. It starts as an Empty storage, a special trivial storage. When we set an element, we then allocate the appropriate storage. Allocations are performed only when needed, so if another element is set then no allocation is performed. More generally, this allows ITensors to have more generic in-place functionality, so you can write code where you don't know what the storage is until runtime.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This can lead to certain types of code having perfomance problems, for example looping through ITensors with many elements can be slow:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> function myscale!(A::ITensor, x::Number)\n for n in 1:dim(A)\n A[n] = x * A[n]\n end\n end;\n\njulia> d = 10_000;\n\njulia> i = Index(d);\n\njulia> @btime myscale!(A, 2) setup = (A = randomITensor(i));\n 2.169 ms (117958 allocations: 3.48 MiB)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"However, this is fast:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> function myscale!(A::Array, x::Number)\n for n in 1:length(A)\n A[n] = x * A[n]\n end\n end;\n\njulia> @btime myscale!(A, 2) setup = (A = randn(d));\n 3.451 μs (0 allocations: 0 bytes)\n\njulia> myscale2!(A::ITensor, x::Number) = myscale!(array(A), x)\nmyscale2! (generic function with 1 method)\n\njulia> @btime myscale2!(A, 2) setup = (A = randomITensor(i));\n 3.571 μs (2 allocations: 112 bytes)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"How does this work? It relies on a \"function barrier\" technique. Julia compiles functions \"just-in-time\", so that calls to an inner function written in terms of a type-stable type are still fast. That inner function is compiled to very fast code. The main overhead is that Julia has to determine which function to call at runtime.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Therefore, users should keep this in mind when they are writing ITensors.jl code, and we warn that explicitly looping over large ITensors by individual elements should be done with caution in performance critical sections of your code. However, be sure to benchmark and profile your code before prematurely optimizing, since you may be surprised about what are the fast and slow parts of your code.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Some strategies for avoiding ITensor loops are:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Use broadcasting and other built-in ITensor functionality that makes use of function barriers.\nConvert ITensors to type-stable collections like the Tensor type of NDTensors.jl and write functions in terms of the Tensor type (i.e. the function barrier techique that is used throughout ITensors.jl).\nWhen initializing very large ITensors elementwise, use built-in ITensor constructors, or first construct an equivalent tensor as an Array or Tensor and then convert it to an ITensor.","category":"page"},{"location":"AdvancedUsageGuide.html#ITensor-in-place-operations","page":"Advanced Usage Guide","title":"ITensor in-place operations","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"In-place operations can help with optimizing code, when the memory of the output tensor of an operation is preallocated.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"The main way to access this in ITensor is through broadcasting. For example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"A = randomITensor(i, i')\nB = randomITensor(i', i)\nA .+= 2 .* B","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Internally, this is rewritten by Julia as a call to broadcast!. ITensors.jl overloads this call (or more specifically, a lower level function copyto! written in terms of a special lazy type that saves all of the objects and operations). Then, this call is rewritten as","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"map!((x,y) -> x+2*y, A, A, B)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This is mostly an optimization to use when you can preallocate storage that can be used multiple times.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Additionally, ITensors makes the unique choice that:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"C .= A .* B","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"is interpreted as an in-place tensor contraction. What this means is that this calls a function:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"mul!(C, A, B)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"(likely to be given an alternative name contract!) which contracts A and B into the pre-allocated memory C.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Because of the design of the ITensor type (see the section above), there is some flexibility we take in allocating memory for users. For example, if the storage type is more narrow than the result, for convenience we might expand it in-place. If you are worried about memory allocations, we recommend using benchmarking and profiling to pinpoint slow parts of your code (often times, you may be surprised by what is actually slow).","category":"page"},{"location":"AdvancedUsageGuide.html#NDTensors-and-ITensors","page":"Advanced Usage Guide","title":"NDTensors and ITensors","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"ITensors.jl is built on top of another, more traditional tensor library called NDTensors. NDTensors implements AbstractArrays with a variety of sparse storage types, with more to come in the future.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"NDTensors implements functionality like permutation of dimensions, fast get and set index, broadcasting, and tensor contraction (where labels of the dimensions must be specified).","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"For example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"using ITensors\nusing NDTensors\n\nT = Tensor(2,2,2)\nT[1,2,1] = 1.3 # Conventional element setting\n\ni = Index(2)\nT = Tensor((i,i',i')) # The identifiers are ignored, just interpreted as above\nT[1,2,1] = 1.3","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"To make performant ITensor code (refer to the the previous section on type stability and function barriers), ITensor storage data and indices are passed by reference into Tensors, where the performance critical operations are performed.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"An example of a function barrier using NDTensors is the following:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using NDTensors\n\njulia> d = 10_000;\n\njulia> i = Index(d);\n\njulia> function myscale!(A::Tensor, x::Number)\n for n in 1:dim(A)\n A[n] = x * A[n]\n end\n end;\n\njulia> @btime myscale!(A, 2) setup = (A = Tensor(d));\n 3.530 μs (0 allocations: 0 bytes)\n\njulia> myscale2!(A::ITensor, x::Number) = myscale!(tensor(A), x)\nmyscale2! (generic function with 1 method)\n\njulia> @btime myscale2!(A, 2) setup = (A = randomITensor(i));\n 3.549 μs (2 allocations: 112 bytes)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"A very efficient function is written for the Tensor type. Then, the ITensor version just wraps the Tensor function by calling it after converting the ITensor to a Tensor (without any copying) with the tensor function. This is the basis for the design of all performance critical ITensors.jl functions.","category":"page"},{"location":"OpSum.html#OpSum","page":"OpSum (AutoMPO)","title":"OpSum","text":"","category":"section"},{"location":"OpSum.html#Description","page":"OpSum (AutoMPO)","title":"Description","text":"","category":"section"},{"location":"OpSum.html","page":"OpSum (AutoMPO)","title":"OpSum (AutoMPO)","text":"OpSum","category":"page"},{"location":"OpSum.html#ITensors.Ops.OpSum","page":"OpSum (AutoMPO)","title":"ITensors.Ops.OpSum","text":"An OpSum represents a sum of operator terms.\n\nOften it is used to create matrix product operator (MPO) approximation of the sum of the terms in the OpSum oject. Each term is a product of local operators specified by names such as \"Sz\" or \"N\", times an optional coefficient which can be real or complex.\n\nWhich local operator names are available is determined by the function op associated with the TagType defined by special Index tags, such as \"S=1/2\", \"S=1\", \"Fermion\", and \"Electron\".\n\n\n\n\n\n","category":"type"},{"location":"OpSum.html#Methods","page":"OpSum (AutoMPO)","title":"Methods","text":"","category":"section"},{"location":"OpSum.html","page":"OpSum (AutoMPO)","title":"OpSum (AutoMPO)","text":"add!\nMPO(::OpSum,::Vector{<:Index})","category":"page"},{"location":"OpSum.html#ITensors.ITensorMPS.add!","page":"OpSum (AutoMPO)","title":"ITensors.ITensorMPS.add!","text":"add!(opsum::OpSum,\n op1::String, i1::Int)\n\nadd!(opsum::OpSum,\n coef::Number,\n op1::String, i1::Int)\n\nadd!(opsum::OpSum,\n op1::String, i1::Int,\n op2::String, i2::Int,\n ops...)\n\nadd!(opsum::OpSum,\n coef::Number,\n op1::String, i1::Int,\n op2::String, i2::Int,\n ops...)\n\n+(opsum:OpSum, term::Tuple)\n\nAdd a single- or multi-site operator term to the OpSum opsum. Each operator is specified by a name (String) and a site number (Int). The second version accepts a real or complex coefficient.\n\nThe + operator version of this function accepts a tuple with entries either (String,Int,String,Int,...) or (Number,String,Int,String,Int,...) where these tuple values are the same as valid inputs to the add! function. For inputting a very large number of terms (tuples) to an OpSum, consider using the broadcasted operator .+= which avoids reallocating the OpSum after each addition.\n\nExamples\n\nopsum = OpSum()\n\nadd!(opsum,\"Sz\",2,\"Sz\",3)\n\nopsum += (\"Sz\",3,\"Sz\",4)\n\nopsum += (0.5,\"S+\",4,\"S-\",5)\n\nopsum .+= (0.5,\"S+\",5,\"S-\",6)\n\n\n\n\n\n","category":"function"},{"location":"OpSum.html#ITensors.ITensorMPS.MPO-Tuple{ITensors.LazyApply.Applied{typeof(sum), Tuple{Array{ITensors.LazyApply.Applied{typeof(*), Tuple{C, Prod{Op}}, @NamedTuple{}}, 1}}, @NamedTuple{}} where C, Vector{<:Index}}","page":"OpSum (AutoMPO)","title":"ITensors.ITensorMPS.MPO","text":"MPO(os::OpSum, sites::Vector{<:Index}; splitblocks=true, kwargs...)\nMPO(eltype::Type{<:Number}, os::OpSum, sites::Vector{<:Index}; splitblocks=true, kwargs...)\n\nConvert an OpSum object os to an MPO, with indices given by sites. The resulting MPO will have the indices sites[1], sites[1]', sites[2], sites[2]' etc. The conversion is done by an algorithm that compresses the MPO resulting from adding the OpSum terms together, often achieving the minimum possible bond dimension.\n\nOptionally specify the desired element type of the output MPO by passing the type as the first argument.\n\nThe keyword argument splitblocks controls the sparsity of the resulting MPO. With the default splitblocks=true, the link indices of the MPO are split into blocks of dimension 1, potentially making the MPO more sparse.\n\nWith the splitblocks=false, the blocks of the link dimensions are packed as much as possible according to common quantum numbers, making larger blocks. Before ITensors 0.3.19, this was the default output, but we have found that in general MPOs output with splitblocks=true lead to better performance in algorithms like DMRG.\n\nExamples\n\nos = OpSum()\nos += \"Sz\",1,\"Sz\",2\nos += \"Sz\",2,\"Sz\",3\nos += \"Sz\",3,\"Sz\",4\n\nsites = siteinds(\"S=1/2\",4)\nH = MPO(os,sites)\nH = MPO(Float32,os,sites)\nH = MPO(os,sites; splitblocks=false)\n\n\n\n\n\n","category":"method"},{"location":"getting_started/Installing.html#Installing-Julia-and-ITensor","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"","category":"section"},{"location":"getting_started/Installing.html#Installing-Julia-Locally-and-On-a-Cluster","page":"Installing Julia and ITensor","title":"Installing Julia Locally and On a Cluster","text":"","category":"section"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Because Julia is a new language, it is usually not pre-installed on machines such as supercomputing clusters. But it is easy to install yourself both on your own machine and in your supercomputing environment. Here we will briefly cover installing Julia on your own machine, then discuss setting it up yourself on a supercomputer.","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Installing Julia on Your Own Machine","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"To install the Julia language, visit https://julialang.org/downloads/ for downloads and installation instructions. Or consider using your system's package manager.","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Cluster Install of Julia and ITensor","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"If you would like to use Julia on a remote cluster, such as at many labs or universities, but Julia is not available system-wide, you can still easily install your own local version of Julia. A local install will offer the same performance and features (package manager, etc.) as a system-wide install, and you can upgrade it at your own pace.","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Once you set up Julia in your cluster account, you can install ITensor in the same way as on your personal computer (see next section on installing ITensor).","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"To install Julia locally within your cluster account, follow these basic steps (details will vary depending on your setup):","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Download a binary version of Julia here. On a remote Unix or Linux cluster, you can use the program wget to download remote files. (Right click on the link on the Julia downloads page to the Generic Linux on x86, 64-bit Julia download to copy the link to pass to the wget program.)\nUse the tar program to uncompress the .tar.gz file you have downloaded.\nCreate a soft link somewhere in your PATH (such as in the bin/ subfolder of your home folder, which you might need to create) pointing to the file \"bin/julia\" inside of the uncompressed Julia folder you just created.","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"For example, the set of commands might look like this (where these commands are assumed to be executed in your home directory):","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"$ cd\n$ mkdir -p bin\n$ wget https://julialang-s3.julialang.org/bin/linux/x64/1.7/julia-1.7.2-linux-x86_64.tar.gz\n$ tar xvzf julia-1.7.2-linux-x86_64.tar.gz\n$ ln -s julia-1.7.2/bin/julia bin/julia","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"If you want to install Julia 1.6.6, you would change 1.7 to 1.6 and 1.7.2 to 1.6.6. In general we recommend using the current stable release of Julia, which you can find out by going to the Julia Downloads page. We also don't recommend using versions of Julia below 1.6, which are no longer compatible with ITensors.jl as of ITensors 0.3.","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"After these steps, you should be able to type julia from your terminal to run Julia in interactive mode. If that works, then you have the Julia language and can run it in all the usual ways. If it does not work, you may need to log out and back in, and check that the bin directory is in your program execution path (PATH environment variable).","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Explanation of the sample commands above:","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"The first command cd goes to your home directory.\nThe second command makes a new folder bin/ under your home directory if it does not already exist.\nThe third command downloads the Julia language as a compressed tar.gz file. (You may want to do this step and the follwing steps in a different folder of your choosing.)\nThe fourth command uncompresses the tar.gz file into a folder called (in this example) julia-1.7.2.\nThe last command makes a soft link called julia in your bin directory which links to the Julia language binary within the folder you just unpacked containing the Julia language.","category":"page"},{"location":"getting_started/Installing.html#Installing-ITensor-(ITensors.jl-Package)","page":"Installing Julia and ITensor","title":"Installing ITensor (ITensors.jl Package)","text":"","category":"section"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Installing the Julia version of ITensor is easy once you have the Julia language installed. For more information about installing Julia, please see the Julia language downloads page.","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Once you have installed Julia on your machine,","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Enter the command julia to launch an interactive Julia session (a.k.a. the Julia \"REPL\")\nType ] to enter the package manager (pkg> prompt should now show)\nEnter the command add ITensors\nAfter installation completes, press backspace to return to the normal julia> prompt\nOptional but Recommended: Enter the command julia> using ITensors; ITensors.compile() to compile a large fraction of the ITensor library code and following the instructions afterward to make an alias for loading a pre-built ITensor system image with Julia. This step can take up to 10 minutes to complete but only has to be done once for each version of ITensor. See the section on compiling ITensor for more information.","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Sample screenshot:","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"(Image: )","category":"page"},{"location":"MPSandMPO.html#MPS-and-MPO","page":"MPS and MPO","title":"MPS and MPO","text":"","category":"section"},{"location":"MPSandMPO.html#Types","page":"MPS and MPO","title":"Types","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"MPS\nMPO","category":"page"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPS","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPS","text":"MPS\n\nA finite size matrix product state type. Keeps track of the orthogonality center.\n\n\n\n\n\n","category":"type"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPO","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPO","text":"MPO\n\nA finite size matrix product operator type. Keeps track of the orthogonality center.\n\n\n\n\n\n","category":"type"},{"location":"MPSandMPO.html#MPS-Constructors","page":"MPS and MPO","title":"MPS Constructors","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"MPS(::Int)\nMPS(::Type{<:Number}, ::Vector{<:Index})\nrandomMPS(sites::Vector{<:Index})\nrandomMPS(::Type{<:Number}, sites::Vector{<:Index})\nrandomMPS(::Vector{<:Index}, ::Any)\nMPS(::Vector{<:Index}, ::Any)\nMPS(::Type{<:Number}, ::Vector{<:Index}, ::Any)\nMPS(::Vector{<:Pair{<:Index}})\nMPS(::Type{<:Number}, ::Vector{<:Pair{<:Index}})","category":"page"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPS-Tuple{Int64}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPS","text":"MPS(N::Int)\n\nConstruct an MPS with N sites with default constructed ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPS-Tuple{Type{<:Number}, Vector{<:Index}}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPS","text":"MPS([::Type{ElT} = Float64, ]sites; linkdims=1)\n\nConstruct an MPS filled with Empty ITensors of type ElT from a collection of indices.\n\nOptionally specify the link dimension with the keyword argument linkdims, which by default is 1.\n\nIn the future we may generalize linkdims to allow specifying each individual link dimension as a vector, and additionally allow specifying quantum numbers.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.randomMPS-Tuple{Vector{<:Index}}","page":"MPS and MPO","title":"ITensors.ITensorMPS.randomMPS","text":"randomMPS(sites::Vector{<:Index}; linkdims=1)\nrandomMPS(eltype::Type{<:Number}, sites::Vector{<:Index}; linkdims=1)\n\nConstruct a random MPS with link dimension linkdims which by default has element type Float64.\n\nlinkdims can also accept a Vector{Int} with length(linkdims) == length(sites) - 1 for constructing an MPS with non-uniform bond dimension.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.randomMPS-Tuple{Type{<:Number}, Vector{<:Index}}","page":"MPS and MPO","title":"ITensors.ITensorMPS.randomMPS","text":"randomMPS(eltype::Type{<:Number}, sites::Vector{<:Index}; linkdims=1)\n\nConstruct a random MPS with link dimension linkdims of type eltype.\n\nlinkdims can also accept a Vector{Int} with length(linkdims) == length(sites) - 1 for constructing an MPS with non-uniform bond dimension.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.randomMPS-Tuple{Vector{<:Index}, Any}","page":"MPS and MPO","title":"ITensors.ITensorMPS.randomMPS","text":"randomMPS(sites::Vector{<:Index}, state; linkdims=1)\n\nConstruct a real, random MPS with link dimension linkdims, made by randomizing an initial product state specified by state. This version of randomMPS is necessary when creating QN-conserving random MPS (consisting of QNITensors). The initial state array provided determines the total QN of the resulting random MPS.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPS-Tuple{Vector{<:Index}, Any}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPS","text":"MPS(sites::Vector{<:Index},states)\n\nConstruct a product state MPS having site indices sites, and which corresponds to the initial state given by the array states. The states array may consist of either an array of integers or strings, as recognized by the state function defined for the relevant Index tag type.\n\nExamples\n\nN = 10\nsites = siteinds(\"S=1/2\", N)\nstates = [isodd(n) ? \"Up\" : \"Dn\" for n in 1:N]\npsi = MPS(sites, states)\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPS-Tuple{Type{<:Number}, Vector{<:Index}, Any}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPS","text":"MPS(::Type{T},\n sites::Vector{<:Index},\n states::Union{Vector{String},\n Vector{Int},\n String,\n Int})\n\nConstruct a product state MPS of element type T, having site indices sites, and which corresponds to the initial state given by the array states. The input states may be an array of strings or an array of ints recognized by the state function defined for the relevant Index tag type. In addition, a single string or int can be input to create a uniform state.\n\nExamples\n\nN = 10\nsites = siteinds(\"S=1/2\", N)\nstates = [isodd(n) ? \"Up\" : \"Dn\" for n in 1:N]\npsi = MPS(ComplexF64, sites, states)\nphi = MPS(sites, \"Up\")\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPS-Tuple{Vector{<:Pair{<:Index}}}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPS","text":"MPS(ivals::Vector{<:Pair{<:Index}})\n\nConstruct a product state MPS with element type Float64 and nonzero values determined from the input IndexVals.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPS-Tuple{Type{<:Number}, Vector{<:Pair{<:Index}}}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPS","text":"MPS(::Type{T<:Number}, ivals::Vector{<:Pair{<:Index}})\n\nConstruct a product state MPS with element type T and nonzero values determined from the input IndexVals.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#MPO-Constructors","page":"MPS and MPO","title":"MPO Constructors","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"MPO(::Int)\nMPO(::Type{<:Number}, ::Vector{<:Index}, ::Vector{String})\nMPO(::Type{<:Number}, ::Vector{<:Index}, ::String)","category":"page"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPO-Tuple{Int64}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPO","text":"MPO(N::Int)\n\nMake an MPO of length N filled with default ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPO-Tuple{Type{<:Number}, Vector{<:Index}, Vector{String}}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPO","text":"MPO([::Type{ElT} = Float64}, ]sites, ops::Vector{String})\n\nMake an MPO with pairs of sites s[i] and s[i]' and operators ops on each site.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPO-Tuple{Type{<:Number}, Vector{<:Index}, String}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPO","text":"MPO([::Type{ElT} = Float64, ]sites, op::String)\n\nMake an MPO with pairs of sites s[i] and s[i]' and operator op on every site.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Copying-behavior","page":"MPS and MPO","title":"Copying behavior","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"copy(::ITensors.AbstractMPS)\ndeepcopy(::ITensors.AbstractMPS)","category":"page"},{"location":"MPSandMPO.html#Base.copy-Tuple{AbstractMPS}","page":"MPS and MPO","title":"Base.copy","text":"copy(::MPS)\ncopy(::MPO)\n\nMake a shallow copy of an MPS or MPO. By shallow copy, it means that a new MPS/MPO is returned, but the data of the tensors are still shared between the returned MPS/MPO and the original MPS/MPO.\n\nTherefore, replacing an entire tensor of the returned MPS/MPO will not modify the input MPS/MPO, but modifying the data of the returned MPS/MPO will modify the input MPS/MPO.\n\nUse deepcopy for an alternative that copies the ITensors as well.\n\nExamples\n\njulia> using ITensors\n\njulia> s = siteinds(\"S=1/2\", 3);\n\njulia> M1 = randomMPS(s; linkdims=3);\n\njulia> norm(M1)\n0.9999999999999999\n\njulia> M2 = copy(M1);\n\njulia> M2[1] *= 2;\n\njulia> norm(M1)\n0.9999999999999999\n\njulia> norm(M2)\n1.9999999999999998\n\njulia> M3 = copy(M1);\n\njulia> M3[1] .*= 3; # Modifies the tensor data\n\njulia> norm(M1)\n3.0000000000000004\n\njulia> norm(M3)\n3.0000000000000004\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Base.deepcopy-Tuple{AbstractMPS}","page":"MPS and MPO","title":"Base.deepcopy","text":"deepcopy(::MPS)\ndeepcopy(::MPO)\n\nMake a deep copy of an MPS or MPO. By deep copy, it means that a new MPS/MPO is returned that doesn't share any data with the input MPS/MPO.\n\nTherefore, modifying the resulting MPS/MPO will note modify the original MPS/MPO.\n\nUse copy for an alternative that performs a shallow copy that avoids copying the ITensor data.\n\nExamples\n\njulia> using ITensors\n\njulia> s = siteinds(\"S=1/2\", 3);\n\njulia> M1 = randomMPS(s; linkdims=3);\n\njulia> norm(M1)\n1.0\n\njulia> M2 = deepcopy(M1);\n\njulia> M2[1] .*= 2; # Modifies the tensor data\n\njulia> norm(M1)\n1.0\n\njulia> norm(M2)\n2.0\n\njulia> M3 = copy(M1);\n\njulia> M3[1] .*= 3; # Modifies the tensor data\n\njulia> norm(M1)\n3.0\n\njulia> norm(M3)\n3.0\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Properties","page":"MPS and MPO","title":"Properties","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"eltype(::ITensors.AbstractMPS)\nflux(::ITensors.AbstractMPS)\nhasqns(::ITensors.AbstractMPS)\nlength(::ITensors.AbstractMPS)\nmaxlinkdim(::ITensors.AbstractMPS)","category":"page"},{"location":"MPSandMPO.html#Base.eltype-Tuple{AbstractMPS}","page":"MPS and MPO","title":"Base.eltype","text":"eltype(m::MPS)\neltype(m::MPO)\n\nThe element type of the MPS/MPO. Always returns ITensor.\n\nFor the element type of the ITensors of the MPS/MPO, use promote_itensor_eltype.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.flux-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.flux","text":"flux(M::MPS)\n\nflux(M::MPO)\n\ntotalqn(M::MPS)\n\ntotalqn(M::MPO)\n\nFor an MPS or MPO which conserves quantum numbers, compute the total QN flux. For a tensor network such as an MPS or MPO, the flux is the sum of fluxes of each of the tensors in the network. The name totalqn is an alias for flux.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.hasqns-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.hasqns","text":"hasqns(M::MPS)\n\nhasqns(M::MPO)\n\nReturn true if the MPS or MPO has tensors which carry quantum numbers.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Base.length-Tuple{AbstractMPS}","page":"MPS and MPO","title":"Base.length","text":"length(::MPS/MPO)\n\nThe number of sites of an MPS/MPO.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.maxlinkdim-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.ITensorMPS.maxlinkdim","text":"maxlinkdim(M::MPS)\nmaxlinkdim(M::MPO)\n\nGet the maximum link dimension of the MPS or MPO.\n\nThe minimum this will return is 1, even if there are no link indices.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Obtaining-and-finding-indices","page":"MPS and MPO","title":"Obtaining and finding indices","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"siteinds(::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS, ::Int)\nsiteinds(::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS, ::Int)\nfindsite\nfindsites\nfirstsiteinds\nlinkind(::ITensors.AbstractMPS,::Int)\nsiteind(::MPS, ::Int)\nsiteind(::typeof(first), ::MPS, ::Int)\nsiteinds(::MPS)\nsiteind(::MPO, ::Int)\nsiteinds(::MPO)\nsiteinds(::ITensors.AbstractMPS, ::Int)","category":"page"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteinds-Tuple{typeof(commoninds), AbstractMPS, AbstractMPS, Int64}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteinds","text":"siteinds(commoninds, A::MPO, B::MPS, j::Integer; kwargs...)\nsiteinds(commonind, A::MPO, B::MPO, j::Integer; kwargs...)\n\nGet the site index (or indices) of the jth MPO tensor of A that is shared with MPS/MPO B.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteinds-Tuple{typeof(uniqueinds), AbstractMPS, AbstractMPS, Int64}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteinds","text":"siteinds(uniqueinds, A::MPO, B::MPS, j::Integer; kwargs...)\nsiteinds(uniqueind, A::MPO, B::MPS, j::Integer; kwargs...)\n\nGet the site index (or indices) of MPO A that is unique to A (not shared with MPS/MPO B).\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.findsite","page":"MPS and MPO","title":"ITensors.ITensorMPS.findsite","text":"findsite(M::Union{MPS, MPO}, is)\n\nReturn the first site of the MPS or MPO that has at least one Index in common with the Index or collection of indices is.\n\nTo find all sites with common indices with is, use the findsites function.\n\nExamples\n\ns = siteinds(\"S=1/2\", 5)\nψ = randomMPS(s)\nfindsite(ψ, s[3]) == 3\nfindsite(ψ, (s[3], s[4])) == 3\n\nM = MPO(s)\nfindsite(M, s[4]) == 4\nfindsite(M, s[4]') == 4\nfindsite(M, (s[4]', s[4])) == 4\nfindsite(M, (s[4]', s[3])) == 3\n\n\n\n\n\n","category":"function"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.findsites","page":"MPS and MPO","title":"ITensors.ITensorMPS.findsites","text":"findsites(M::Union{MPS, MPO}, is)\n\nReturn the sites of the MPS or MPO that have indices in common with the collection of site indices is.\n\nExamples\n\ns = siteinds(\"S=1/2\", 5)\nψ = randomMPS(s)\nfindsites(ψ, s[3]) == [3]\nfindsites(ψ, (s[4], s[1])) == [1, 4]\n\nM = MPO(s)\nfindsites(M, s[4]) == [4]\nfindsites(M, s[4]') == [4]\nfindsites(M, (s[4]', s[4])) == [4]\nfindsites(M, (s[4]', s[3])) == [3, 4]\n\n\n\n\n\n","category":"function"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.firstsiteinds","page":"MPS and MPO","title":"ITensors.ITensorMPS.firstsiteinds","text":"firstsiteinds(M::MPO; kwargs...)\n\nGet a Vector of the first site Index found on each site of M.\n\nBy default, it finds the first site Index with prime level 0.\n\n\n\n\n\n","category":"function"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.linkind-Tuple{AbstractMPS, Int64}","page":"MPS and MPO","title":"ITensors.ITensorMPS.linkind","text":"linkind(M::MPS, j::Integer)\nlinkind(M::MPO, j::Integer)\n\nGet the link or bond Index connecting the MPS or MPO tensor on site j to site j+1.\n\nIf there is no link Index, return nothing.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteind-Tuple{MPS, Int64}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteind","text":"siteind(M::MPS, j::Int; kwargs...)\n\nGet the first site Index of the MPS. Return nothing if none is found.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteind-Tuple{typeof(first), MPS, Int64}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteind","text":"siteind(::typeof(first), M::Union{MPS,MPO}, j::Integer; kwargs...)\n\nReturn the first site Index found on the MPS or MPO (the first Index unique to the jth MPS/MPO tensor).\n\nYou can choose different filters, like prime level and tags, with the kwargs.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteinds-Tuple{MPS}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteinds","text":"siteinds(M::MPS)\nsiteinds(::typeof(first), M::MPS)\n\nGet a vector of the first site Index found on each tensor of the MPS.\n\nsiteinds(::typeof(only), M::MPS)\n\nGet a vector of the only site Index found on each tensor of the MPS. Errors if more than one is found.\n\nsiteinds(::typeof(all), M::MPS)\n\nGet a vector of the all site Indices found on each tensor of the MPS. Returns a Vector of IndexSets.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteind-Tuple{MPO, Int64}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteind","text":"siteind(M::MPO, j::Int; plev = 0, kwargs...)\n\nGet the first site Index of the MPO found, by default with prime level 0.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteinds-Tuple{MPO}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteinds","text":"siteinds(M::MPO; kwargs...)\n\nGet a Vector of IndexSets of all the site indices of M.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteinds-Tuple{AbstractMPS, Int64}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteinds","text":"siteinds(M::Union{MPS, MPO}}, j::Integer; kwargs...)\n\nReturn the site Indices found of the MPO or MPO at the site j as an IndexSet.\n\nOptionally filter prime tags and prime levels with keyword arguments like plev and tags.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Priming-and-tagging","page":"MPS and MPO","title":"Priming and tagging","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"prime(::ITensors.AbstractMPS)\nprime(::typeof(siteinds), ::ITensors.AbstractMPS)\nprime(::typeof(linkinds), ::ITensors.AbstractMPS)\nprime(::typeof(siteinds), ::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\nprime(::typeof(siteinds), ::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\n\nswapprime(::ITensors.AbstractMPS, args...; kwargs...)\n\nsetprime(::ITensors.AbstractMPS)\nsetprime(::typeof(siteinds), ::ITensors.AbstractMPS)\nsetprime(::typeof(linkinds), ::ITensors.AbstractMPS)\nsetprime(::typeof(siteinds), ::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\nsetprime(::typeof(siteinds), ::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\n\nnoprime(::ITensors.AbstractMPS)\nnoprime(::typeof(siteinds), ::ITensors.AbstractMPS)\nnoprime(::typeof(linkinds), ::ITensors.AbstractMPS)\nnoprime(::typeof(siteinds), ::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\nnoprime(::typeof(siteinds), ::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\n\naddtags(::ITensors.AbstractMPS)\naddtags(::typeof(siteinds), ::ITensors.AbstractMPS)\naddtags(::typeof(linkinds), ::ITensors.AbstractMPS)\naddtags(::typeof(siteinds), ::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\naddtags(::typeof(siteinds), ::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\n\nremovetags(::ITensors.AbstractMPS)\nremovetags(::typeof(siteinds), ::ITensors.AbstractMPS)\nremovetags(::typeof(linkinds), ::ITensors.AbstractMPS)\nremovetags(::typeof(siteinds), ::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\nremovetags(::typeof(siteinds), ::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\n\nreplacetags(::ITensors.AbstractMPS)\nreplacetags(::typeof(siteinds), ::ITensors.AbstractMPS)\nreplacetags(::typeof(linkinds), ::ITensors.AbstractMPS)\nreplacetags(::typeof(siteinds), ::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\nreplacetags(::typeof(siteinds), ::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\n\nsettags(::ITensors.AbstractMPS)\nsettags(::typeof(siteinds), ::ITensors.AbstractMPS)\nsettags(::typeof(linkinds), ::ITensors.AbstractMPS)\nsettags(::typeof(siteinds), ::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\nsettags(::typeof(siteinds), ::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)","category":"page"},{"location":"MPSandMPO.html#ITensors.prime-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.prime","text":"prime[!](M::MPS, args...; kwargs...)\nprime[!](M::MPO, args...; kwargs...)\n\nApply prime to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.prime-Tuple{typeof(siteinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.prime","text":"prime[!](siteinds, M::MPS, args...; kwargs...)\nprime[!](siteinds, M::MPO, args...; kwargs...)\n\nApply prime to all site indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.prime-Tuple{typeof(linkinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.prime","text":"prime[!](linkinds, M::MPS, args...; kwargs...)\nprime[!](linkinds, M::MPO, args...; kwargs...)\n\nApply prime to all link indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.prime-Tuple{typeof(siteinds), typeof(commoninds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.prime","text":"prime[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)\nprime[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)\n\nApply prime to the site indices that are shared by M1 and M2.\n\nReturns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.prime-Tuple{typeof(siteinds), typeof(uniqueinds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.prime","text":"prime[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)\n\nApply prime to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.\n\nThe ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.swapprime-Tuple{AbstractMPS, Vararg{Any}}","page":"MPS and MPO","title":"ITensors.swapprime","text":"swapprime[!](M::MPS, args...; kwargs...)\nswapprime[!](M::MPO, args...; kwargs...)\n\nApply swapprime to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.setprime-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.setprime","text":"setprime[!](M::MPS, args...; kwargs...)\nsetprime[!](M::MPO, args...; kwargs...)\n\nApply setprime to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.setprime-Tuple{typeof(siteinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.setprime","text":"setprime[!](siteinds, M::MPS, args...; kwargs...)\nsetprime[!](siteinds, M::MPO, args...; kwargs...)\n\nApply setprime to all site indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.setprime-Tuple{typeof(linkinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.setprime","text":"setprime[!](linkinds, M::MPS, args...; kwargs...)\nsetprime[!](linkinds, M::MPO, args...; kwargs...)\n\nApply setprime to all link indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.setprime-Tuple{typeof(siteinds), typeof(commoninds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.setprime","text":"setprime[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)\nsetprime[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)\n\nApply setprime to the site indices that are shared by M1 and M2.\n\nReturns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.setprime-Tuple{typeof(siteinds), typeof(uniqueinds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.setprime","text":"setprime[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)\n\nApply setprime to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.\n\nThe ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.noprime-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.noprime","text":"noprime[!](M::MPS, args...; kwargs...)\nnoprime[!](M::MPO, args...; kwargs...)\n\nApply noprime to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.noprime-Tuple{typeof(siteinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.noprime","text":"noprime[!](siteinds, M::MPS, args...; kwargs...)\nnoprime[!](siteinds, M::MPO, args...; kwargs...)\n\nApply noprime to all site indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.noprime-Tuple{typeof(linkinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.noprime","text":"noprime[!](linkinds, M::MPS, args...; kwargs...)\nnoprime[!](linkinds, M::MPO, args...; kwargs...)\n\nApply noprime to all link indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.noprime-Tuple{typeof(siteinds), typeof(commoninds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.noprime","text":"noprime[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)\nnoprime[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)\n\nApply noprime to the site indices that are shared by M1 and M2.\n\nReturns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.noprime-Tuple{typeof(siteinds), typeof(uniqueinds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.noprime","text":"noprime[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)\n\nApply noprime to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.\n\nThe ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.addtags-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.addtags","text":"addtags[!](M::MPS, args...; kwargs...)\naddtags[!](M::MPO, args...; kwargs...)\n\nApply addtags to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.addtags-Tuple{typeof(siteinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.addtags","text":"addtags[!](siteinds, M::MPS, args...; kwargs...)\naddtags[!](siteinds, M::MPO, args...; kwargs...)\n\nApply addtags to all site indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.addtags-Tuple{typeof(linkinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.addtags","text":"addtags[!](linkinds, M::MPS, args...; kwargs...)\naddtags[!](linkinds, M::MPO, args...; kwargs...)\n\nApply addtags to all link indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.addtags-Tuple{typeof(siteinds), typeof(commoninds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.addtags","text":"addtags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)\naddtags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)\n\nApply addtags to the site indices that are shared by M1 and M2.\n\nReturns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.addtags-Tuple{typeof(siteinds), typeof(uniqueinds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.addtags","text":"addtags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)\n\nApply addtags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.\n\nThe ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.removetags-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.removetags","text":"removetags[!](M::MPS, args...; kwargs...)\nremovetags[!](M::MPO, args...; kwargs...)\n\nApply removetags to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.removetags-Tuple{typeof(siteinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.removetags","text":"removetags[!](siteinds, M::MPS, args...; kwargs...)\nremovetags[!](siteinds, M::MPO, args...; kwargs...)\n\nApply removetags to all site indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.removetags-Tuple{typeof(linkinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.removetags","text":"removetags[!](linkinds, M::MPS, args...; kwargs...)\nremovetags[!](linkinds, M::MPO, args...; kwargs...)\n\nApply removetags to all link indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.removetags-Tuple{typeof(siteinds), typeof(commoninds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.removetags","text":"removetags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)\nremovetags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)\n\nApply removetags to the site indices that are shared by M1 and M2.\n\nReturns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.removetags-Tuple{typeof(siteinds), typeof(uniqueinds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.removetags","text":"removetags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)\n\nApply removetags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.\n\nThe ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.replacetags-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.replacetags","text":"replacetags[!](M::MPS, args...; kwargs...)\nreplacetags[!](M::MPO, args...; kwargs...)\n\nApply replacetags to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.replacetags-Tuple{typeof(siteinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.replacetags","text":"replacetags[!](siteinds, M::MPS, args...; kwargs...)\nreplacetags[!](siteinds, M::MPO, args...; kwargs...)\n\nApply replacetags to all site indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.replacetags-Tuple{typeof(linkinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.replacetags","text":"replacetags[!](linkinds, M::MPS, args...; kwargs...)\nreplacetags[!](linkinds, M::MPO, args...; kwargs...)\n\nApply replacetags to all link indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.replacetags-Tuple{typeof(siteinds), typeof(commoninds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.replacetags","text":"replacetags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)\nreplacetags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)\n\nApply replacetags to the site indices that are shared by M1 and M2.\n\nReturns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.replacetags-Tuple{typeof(siteinds), typeof(uniqueinds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.replacetags","text":"replacetags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)\n\nApply replacetags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.\n\nThe ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.settags-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.settags","text":"settags[!](M::MPS, args...; kwargs...)\nsettags[!](M::MPO, args...; kwargs...)\n\nApply settags to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.settags-Tuple{typeof(siteinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.settags","text":"settags[!](siteinds, M::MPS, args...; kwargs...)\nsettags[!](siteinds, M::MPO, args...; kwargs...)\n\nApply settags to all site indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.settags-Tuple{typeof(linkinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.settags","text":"settags[!](linkinds, M::MPS, args...; kwargs...)\nsettags[!](linkinds, M::MPO, args...; kwargs...)\n\nApply settags to all link indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.settags-Tuple{typeof(siteinds), typeof(commoninds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.settags","text":"settags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)\nsettags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)\n\nApply settags to the site indices that are shared by M1 and M2.\n\nReturns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.settags-Tuple{typeof(siteinds), typeof(uniqueinds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.settags","text":"settags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)\n\nApply settags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.\n\nThe ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Operations","page":"MPS and MPO","title":"Operations","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"expect(::MPS, ::Any)\ncorrelation_matrix(::MPS, ::AbstractString, ::AbstractString)\ndag(::ITensors.AbstractMPS)\ndense(::ITensors.AbstractMPS)\nmovesite(::ITensors.AbstractMPS, ::Pair{Int, Int};orthocenter::Int,kwargs...)\northogonalize!\nreplacebond!(::MPS, ::Int, ::ITensor)\nsample(::MPS)\nsample!(::MPS)\nsample(::MPO)\nswapbondsites(::ITensors.AbstractMPS, ::Int; kwargs...)\ntruncate!","category":"page"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.expect-Tuple{MPS, Any}","page":"MPS and MPO","title":"ITensors.ITensorMPS.expect","text":"expect(psi::MPS, op::AbstractString...; kwargs...)\nexpect(psi::MPS, op::Matrix{<:Number}...; kwargs...)\nexpect(psi::MPS, ops; kwargs...)\n\nGiven an MPS psi and a single operator name, returns a vector of the expected value of the operator on each site of the MPS.\n\nIf multiple operator names are provided, returns a tuple of expectation value vectors.\n\nIf a container of operator names is provided, returns the same type of container with names replaced by vectors of expectation values.\n\nOptional Keyword Arguments\n\nsites = 1:length(psi): compute expected values only for sites in the given range\n\nExamples\n\nN = 10\n\ns = siteinds(\"S=1/2\", N)\npsi = randomMPS(s; linkdims=8)\nZ = expect(psi, \"Sz\") # compute for all sites\nZ = expect(psi, \"Sz\"; sites=2:4) # compute for sites 2,3,4\nZ3 = expect(psi, \"Sz\"; sites=3) # compute for site 3 only (output will be a scalar)\nXZ = expect(psi, [\"Sx\", \"Sz\"]) # compute Sx and Sz for all sites\nZ = expect(psi, [1/2 0; 0 -1/2]) # same as expect(psi,\"Sz\")\n\ns = siteinds(\"Electron\", N)\npsi = randomMPS(s; linkdims=8)\ndens = expect(psi, \"Ntot\")\nupdens, dndens = expect(psi, \"Nup\", \"Ndn\") # pass more than one operator\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.correlation_matrix-Tuple{MPS, AbstractString, AbstractString}","page":"MPS and MPO","title":"ITensors.ITensorMPS.correlation_matrix","text":"correlation_matrix(psi::MPS,\n Op1::AbstractString,\n Op2::AbstractString;\n kwargs...)\n\ncorrelation_matrix(psi::MPS,\n Op1::Matrix{<:Number},\n Op2::Matrix{<:Number};\n kwargs...)\n\nGiven an MPS psi and two strings denoting operators (as recognized by the op function), computes the two-point correlation function matrix C[i,j] = using efficient MPS techniques. Returns the matrix C.\n\nOptional Keyword Arguments\n\nsites = 1:length(psi): compute correlations only for sites in the given range\nishermitian = false : if false, force independent calculations of the matrix elements above and below the diagonal, while if true assume they are complex conjugates.\n\nFor a correlation matrix of size NxN and an MPS of typical bond dimension m, the scaling of this algorithm is N^2*m^3.\n\nExamples\n\nN = 30\nm = 4\n\ns = siteinds(\"S=1/2\", N)\npsi = randomMPS(s; linkdims=m)\nCzz = correlation_matrix(psi, \"Sz\", \"Sz\")\nCzz = correlation_matrix(psi, [1/2 0; 0 -1/2], [1/2 0; 0 -1/2]) # same as above\n\ns = siteinds(\"Electron\", N; conserve_qns=true)\npsi = randomMPS(s, n -> isodd(n) ? \"Up\" : \"Dn\"; linkdims=m)\nCuu = correlation_matrix(psi, \"Cdagup\", \"Cup\"; sites=2:8)\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.dag-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.dag","text":"dag[!](M::MPS, args...; kwargs...)\ndag[!](M::MPO, args...; kwargs...)\n\nApply dag to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#NDTensors.dense-Tuple{AbstractMPS}","page":"MPS and MPO","title":"NDTensors.dense","text":"dense(::MPS/MPO)\n\nGiven an MPS (or MPO), return a new MPS (or MPO) having called dense on each ITensor to convert each tensor to use dense storage and remove any QN or other sparse structure information, if it is not dense already.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.movesite-Tuple{AbstractMPS, Pair{Int64, Int64}}","page":"MPS and MPO","title":"ITensors.ITensorMPS.movesite","text":"movesite(::Union{MPS, MPO}, n1n2::Pair{Int, Int})\n\nCreate a new MPS/MPO where the site at n1 is moved to n2, for a pair n1n2 = n1 => n2.\n\nThis is done with a series a pairwise swaps, and can introduce a lot of entanglement into your state, so use with caution.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.orthogonalize!","page":"MPS and MPO","title":"ITensors.ITensorMPS.orthogonalize!","text":"orthogonalize!(M::MPS, j::Int; kwargs...)\northogonalize(M::MPS, j::Int; kwargs...)\n\northogonalize!(M::MPO, j::Int; kwargs...)\northogonalize(M::MPO, j::Int; kwargs...)\n\nMove the orthogonality center of the MPS to site j. No observable property of the MPS will be changed, and no truncation of the bond indices is performed. Afterward, tensors 1,2,...,j-1 will be left-orthogonal and tensors j+1,j+2,...,N will be right-orthogonal.\n\nEither modify in-place with orthogonalize! or out-of-place with orthogonalize.\n\n\n\n\n\n","category":"function"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.replacebond!-Tuple{MPS, Int64, ITensor}","page":"MPS and MPO","title":"ITensors.ITensorMPS.replacebond!","text":"replacebond!(M::MPS, b::Int, phi::ITensor; kwargs...)\n\nFactorize the ITensor phi and replace the ITensors b and b+1 of MPS M with the factors. Choose the orthogonality with ortho=\"left\"/\"right\".\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.sample-Tuple{MPS}","page":"MPS and MPO","title":"ITensors.ITensorMPS.sample","text":"sample(m::MPS)\n\nGiven a normalized MPS m with orthocenter(m)==1, returns a Vector{Int} of length(m) corresponding to one sample of the probability distribution defined by squaring the components of the tensor that the MPS represents\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.sample!-Tuple{MPS}","page":"MPS and MPO","title":"ITensors.ITensorMPS.sample!","text":"sample!(m::MPS)\n\nGiven a normalized MPS m, returns a Vector{Int} of length(m) corresponding to one sample of the probability distribution defined by squaring the components of the tensor that the MPS represents. If the MPS does not have an orthogonality center, orthogonalize!(m,1) will be called before computing the sample.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.sample-Tuple{MPO}","page":"MPS and MPO","title":"ITensors.ITensorMPS.sample","text":"sample(M::MPO)\n\nGiven a normalized MPO M, returns a Vector{Int} of length(M) corresponding to one sample of the probability distribution defined by the MPO, treating the MPO as a density matrix.\n\nThe MPO M should have an (approximately) positive spectrum.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.swapbondsites-Tuple{AbstractMPS, Int64}","page":"MPS and MPO","title":"ITensors.ITensorMPS.swapbondsites","text":"swapbondsites(ψ::Union{MPS, MPO}, b::Integer; kwargs...)\n\nSwap the sites b and b+1.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#NDTensors.truncate!","page":"MPS and MPO","title":"NDTensors.truncate!","text":"truncate!(M::MPS; kwargs...)\ntruncate!(M::MPO; kwargs...)\n\nPerform a truncation of all bonds of an MPS/MPO, using the truncation parameters (cutoff,maxdim, etc.) provided as keyword arguments.\n\nKeyword arguments:\n\nsite_range=1:N - only truncate the MPS bonds between these sites\n\n\n\n\n\n","category":"function"},{"location":"MPSandMPO.html#Gate-evolution","page":"MPS and MPO","title":"Gate evolution","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"product(::ITensor, ::ITensors.AbstractMPS)\nproduct(::Vector{ITensor}, ::ITensors.AbstractMPS)","category":"page"},{"location":"MPSandMPO.html#ITensors.product-Tuple{ITensor, AbstractMPS}","page":"MPS and MPO","title":"ITensors.product","text":"apply(o::ITensor, ψ::Union{MPS, MPO}, [ns::Vector{Int}]; kwargs...)\nproduct([...])\n\nGet the product of the operator o with the MPS/MPO ψ, where the operator is applied to the sites ns. If ns are not specified, the sites are determined by the common indices between o and the site indices of ψ.\n\nIf ns are non-contiguous, the sites of the MPS are moved to be contiguous. By default, the sites are moved back to their original locations. You can leave them where they are by setting the keyword argument move_sites_back to false.\n\nKeywords\n\ncutoff::Real: singular value truncation cutoff.\nmaxdim::Int: maximum MPS/MPO dimension.\napply_dag::Bool = false: apply the gate and the dagger of the gate (only relevant for MPO evolution).\nmove_sites_back::Bool = true: after the ITensors are applied to the MPS or MPO, move the sites of the MPS or MPO back to their original locations.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.product-Tuple{Vector{ITensor}, AbstractMPS}","page":"MPS and MPO","title":"ITensors.product","text":"apply(As::Vector{<:ITensor}, M::Union{MPS, MPO}; kwargs...)\nproduct([...])\n\nApply the ITensors As to the MPS or MPO M, treating them as gates or matrices from pairs of prime or unprimed indices.\n\nKeywords\n\ncutoff::Real: singular value truncation cutoff.\nmaxdim::Int: maximum MPS/MPO dimension.\napply_dag::Bool = false: apply the gate and the dagger of the gate (only relevant for MPO evolution).\nmove_sites_back::Bool = true: after the ITensor is applied to the MPS or MPO, move the sites of the MPS or MPO back to their original locations.\n\nExamples\n\nApply one-site gates to an MPS:\n\nN = 3\n\nITensors.op(::OpName\"σx\", ::SiteType\"S=1/2\", s::Index) =\n 2*op(\"Sx\", s)\n\nITensors.op(::OpName\"σz\", ::SiteType\"S=1/2\", s::Index) =\n 2*op(\"Sz\", s)\n\n# Make the operator list.\nos = [(\"σx\", n) for n in 1:N]\nappend!(os, [(\"σz\", n) for n in 1:N])\n\n@show os\n\ns = siteinds(\"S=1/2\", N)\ngates = ops(os, s)\n\n# Starting state |↑↑↑⟩\nψ0 = MPS(s, \"↑\")\n\n# Apply the gates.\nψ = apply(gates, ψ0; cutoff = 1e-15)\n\n# Test against exact (full) wavefunction\nprodψ = apply(gates, prod(ψ0))\n@show prod(ψ) ≈ prodψ\n\n# The result is:\n# σz₃ σz₂ σz₁ σx₃ σx₂ σx₁ |↑↑↑⟩ = -|↓↓↓⟩\n@show inner(ψ, MPS(s, \"↓\")) == -1\n\nApply nonlocal two-site gates and one-site gates to an MPS:\n\n# 2-site gate\nfunction ITensors.op(::OpName\"CX\", ::SiteType\"S=1/2\", s1::Index, s2::Index)\n mat = [1 0 0 0\n 0 1 0 0\n 0 0 0 1\n 0 0 1 0]\n return itensor(mat, s2', s1', s2, s1)\nend\n\nos = [(\"CX\", 1, 3), (\"σz\", 3)]\n\n@show os\n\n# Start with the state |↓↑↑⟩\nψ0 = MPS(s, n -> n == 1 ? \"↓\" : \"↑\")\n\n# The result is:\n# σz₃ CX₁₃ |↓↑↑⟩ = -|↓↑↓⟩\nψ = apply(ops(os, s), ψ0; cutoff = 1e-15)\n@show inner(ψ, MPS(s, n -> n == 1 || n == 3 ? \"↓\" : \"↑\")) == -1\n\nPerform TEBD-like time evolution:\n\n# Define the nearest neighbor term `S⋅S` for the Heisenberg model\nfunction ITensors.op(::OpName\"expS⋅S\", ::SiteType\"S=1/2\",\n s1::Index, s2::Index; τ::Number)\n O = 0.5 * op(\"S+\", s1) * op(\"S-\", s2) +\n 0.5 * op(\"S-\", s1) * op(\"S+\", s2) +\n op(\"Sz\", s1) * op(\"Sz\", s2)\n return exp(τ * O)\nend\n\nτ = -0.1im\nos = [(\"expS⋅S\", (1, 2), (τ = τ,)),\n (\"expS⋅S\", (2, 3), (τ = τ,))]\nψ0 = MPS(s, n -> n == 1 ? \"↓\" : \"↑\")\nexpτH = ops(os, s)\nψτ = apply(expτH, ψ0)\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Algebra-Operations","page":"MPS and MPO","title":"Algebra Operations","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"inner(::MPST, ::MPST) where {MPST <: ITensors.AbstractMPS}\ndot(::MPST, ::MPST) where {MPST <: ITensors.AbstractMPS}\nloginner(::MPST, ::MPST) where {MPST <: ITensors.AbstractMPS}\nlogdot(::MPST, ::MPST) where {MPST <: ITensors.AbstractMPS}\ninner(::MPS, ::MPO, ::MPS)\ndot(::MPS, ::MPO, ::MPS)\ninner(::MPO, ::MPS, ::MPO, ::MPS)\ndot(::MPO, ::MPS, ::MPO, ::MPS)\nnorm(::ITensors.AbstractMPS)\nnormalize(::ITensors.AbstractMPS)\nnormalize!(::ITensors.AbstractMPS)\nlognorm(::ITensors.AbstractMPS)\n+(::ITensors.AbstractMPS...)\ncontract(::MPO, ::MPS)\napply(::MPO, ::MPS)\ncontract(::MPO, ::MPO)\napply(::MPO, ::MPO)\nerror_contract(y::MPS, A::MPO, x::MPS)\nouter(::MPS, ::MPS)\nprojector(::MPS)","category":"page"},{"location":"MPSandMPO.html#ITensors.inner-Union{Tuple{MPST}, Tuple{MPST, MPST}} where MPST<:AbstractMPS","page":"MPS and MPO","title":"ITensors.inner","text":"inner(A::MPS, B::MPS)\ninner(A::MPO, B::MPO)\n\nCompute the inner product ⟨A|B⟩. If A and B are MPOs, computes the Frobenius inner product.\n\nUse loginner to avoid underflow/overflow for taking overlaps of large MPS or MPO.\n\ncompat: ITensors 0.3\n\n\nBefore ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.\n\nSame as dot.\n\nSee also loginner, logdot.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#LinearAlgebra.dot-Union{Tuple{MPST}, Tuple{MPST, MPST}} where MPST<:AbstractMPS","page":"MPS and MPO","title":"LinearAlgebra.dot","text":"dot(A::MPS, B::MPS)\ndot(A::MPO, B::MPO)\n\nSame as inner.\n\nSee also loginner, logdot.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.loginner-Union{Tuple{MPST}, Tuple{MPST, MPST}} where MPST<:AbstractMPS","page":"MPS and MPO","title":"ITensors.ITensorMPS.loginner","text":"loginner(A::MPS, B::MPS)\nloginner(A::MPO, B::MPO)\n\nCompute the logarithm of the inner product ⟨A|B⟩. If A and B are MPOs, computes the logarithm of the Frobenius inner product.\n\nThis is useful for larger MPS/MPO, where in the limit of large numbers of sites the inner product can diverge or approach zero.\n\ncompat: ITensors 0.3\n\n\nBefore ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.\n\nSame as logdot.\n\nSee also inner, dot.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.logdot-Union{Tuple{MPST}, Tuple{MPST, MPST}} where MPST<:AbstractMPS","page":"MPS and MPO","title":"ITensors.ITensorMPS.logdot","text":"logdot(A::MPS, B::MPS)\nlogdot(A::MPO, B::MPO)\n\nSame as loginner.\n\nSee also inner, dot.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.inner-Tuple{MPS, MPO, MPS}","page":"MPS and MPO","title":"ITensors.inner","text":"inner(y::MPS, A::MPO, x::MPS)\n\nCompute ⟨y|A|x⟩ = ⟨y|Ax⟩ efficiently and exactly without making any intermediate MPOs. In general it is more efficient and accurate than inner(y, apply(A, x)).\n\nThis is helpful for computing the expectation value of an operator A, which would be:\n\ninner(x', A, x)\n\nassuming x is normalized.\n\nIf you want to compute ⟨By|Ax⟩ you can use inner(B::MPO, y::MPS, A::MPO, x::MPS).\n\nThis is helpful for computing the variance of an operator A, which would be:\n\ninner(A, x, A, x) - inner(x', A, x) ^ 2\n\nassuming x is normalized.\n\ncompat: ITensors 0.3\n\n\nBefore ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.\n\nSame as dot.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#LinearAlgebra.dot-Tuple{MPS, MPO, MPS}","page":"MPS and MPO","title":"LinearAlgebra.dot","text":"dot(y::MPS, A::MPO, x::MPS)\n\nSame as inner.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.inner-Tuple{MPO, MPS, MPO, MPS}","page":"MPS and MPO","title":"ITensors.inner","text":"inner(B::MPO, y::MPS, A::MPO, x::MPS)\n\nCompute ⟨By|A|x⟩ = ⟨By|Ax⟩ efficiently and exactly without making any intermediate MPOs. In general it is more efficient and accurate than inner(apply(B, y), apply(A, x)).\n\nThis is helpful for computing the variance of an operator A, which would be:\n\ninner(A, x, A, x) - inner(x, A, x) ^ 2\n\ncompat: ITensors 0.3\n\n\nBefore ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.\n\nSame as dot.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#LinearAlgebra.dot-Tuple{MPO, MPS, MPO, MPS}","page":"MPS and MPO","title":"LinearAlgebra.dot","text":"dot(B::MPO, y::MPS, A::MPO, x::MPS)\n\nSame as inner.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#LinearAlgebra.norm-Tuple{AbstractMPS}","page":"MPS and MPO","title":"LinearAlgebra.norm","text":"norm(A::MPS)\nnorm(A::MPO)\n\nCompute the norm of the MPS or MPO.\n\nIf the MPS or MPO has a well defined orthogonality center, this reduces to the norm of the orthogonality center tensor. Otherwise, it computes the norm with the full inner product of the MPS/MPO with itself.\n\nSee also lognorm.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#LinearAlgebra.normalize-Tuple{AbstractMPS}","page":"MPS and MPO","title":"LinearAlgebra.normalize","text":"normalize(A::MPS; (lognorm!)=[])\nnormalize(A::MPO; (lognorm!)=[])\n\nReturn a new MPS or MPO A that is the same as the original MPS or MPO but with norm(A) ≈ 1.\n\nIn practice, this evenly spreads lognorm(A) over the tensors within the range of the orthogonality center to avoid numerical overflow in the case of diverging norms.\n\nSee also normalize!, norm, lognorm.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#LinearAlgebra.normalize!-Tuple{AbstractMPS}","page":"MPS and MPO","title":"LinearAlgebra.normalize!","text":"normalize!(A::MPS; (lognorm!)=[])\nnormalize!(A::MPO; (lognorm!)=[])\n\nChange the MPS or MPO A in-place such that norm(A) ≈ 1. This modifies the data of the tensors within the orthogonality center.\n\nIn practice, this evenly spreads lognorm(A) over the tensors within the range of the orthogonality center to avoid numerical overflow in the case of diverging norms.\n\nIf the norm of the input MPS or MPO is 0, normalizing is ill-defined. In this case, we just return the original MPS or MPO. You can check for this case as follows:\n\ns = siteinds(\"S=1/2\", 4)\nψ = 0 * randomMPS(s)\nlognorm_ψ = []\nnormalize!(ψ; (lognorm!)=lognorm_ψ)\nlognorm_ψ[1] == -Inf # There was an infinite norm\n\nSee also normalize, norm, lognorm.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.lognorm-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.ITensorMPS.lognorm","text":"lognorm(A::MPS)\nlognorm(A::MPO)\n\nCompute the logarithm of the norm of the MPS or MPO.\n\nThis is useful for larger MPS/MPO that are not gauged, where in the limit of large numbers of sites the norm can diverge or approach zero.\n\nSee also norm, logdot.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Base.:+-Tuple{Vararg{AbstractMPS}}","page":"MPS and MPO","title":"Base.:+","text":"+(A::MPS/MPO...; kwargs...)\nadd(A::MPS/MPO...; kwargs...)\n\nAdd arbitrary numbers of MPS/MPO with each other, optionally truncating the results.\n\nA cutoff of 1e-15 is used by default, and in general users should set their own cutoff for their particular application.\n\nKeywords\n\ncutoff::Real: singular value truncation cutoff\nmaxdim::Int: maximum MPS/MPO bond dimension\nalg = \"densitymatrix\": \"densitymatrix\" or \"directsum\". \"densitymatrix\" adds the MPS/MPO by adding up and diagoanlizing local density matrices site by site in a single sweep through the system, truncating the density matrix with cutoff and maxdim. \"directsum\" performs a direct sum of each tensors on each site of the input MPS/MPO being summed. It doesn't perform any truncation, and therefore ignores cutoff and maxdim. The bond dimension of the output is the sum of the bond dimensions of the inputs. You can truncate the resulting MPS/MPO with the truncate! function.\n\nExamples\n\nN = 10\n\ns = siteinds(\"S=1/2\", N; conserve_qns = true)\n\nstate = n -> isodd(n) ? \"↑\" : \"↓\"\nψ₁ = randomMPS(s, state, 2)\nψ₂ = randomMPS(s, state, 2)\nψ₃ = randomMPS(s, state, 2)\n\nψ = +(ψ₁, ψ₂; cutoff = 1e-8)\n\n# Can use:\n#\n# ψ = ψ₁ + ψ₂\n#\n# but generally you want to set a custom `cutoff` and `maxdim`.\n\nprintln()\n@show inner(ψ, ψ)\n@show inner(ψ₁, ψ₂) + inner(ψ₁, ψ₂) + inner(ψ₂, ψ₁) + inner(ψ₂, ψ₂)\n\n# Computes ψ₁ + 2ψ₂\nψ = ψ₁ + 2ψ₂\n\nprintln()\n@show inner(ψ, ψ)\n@show inner(ψ₁, ψ₁) + 2 * inner(ψ₁, ψ₂) + 2 * inner(ψ₂, ψ₁) + 4 * inner(ψ₂, ψ₂)\n\n# Computes ψ₁ + 2ψ₂ + ψ₃\nψ = ψ₁ + 2ψ₂ + ψ₃\n\nprintln()\n@show inner(ψ, ψ)\n@show inner(ψ₁, ψ₁) + 2 * inner(ψ₁, ψ₂) + inner(ψ₁, ψ₃) +\n 2 * inner(ψ₂, ψ₁) + 4 * inner(ψ₂, ψ₂) + 2 * inner(ψ₂, ψ₃) +\n inner(ψ₃, ψ₁) + 2 * inner(ψ₃, ψ₂) + inner(ψ₃, ψ₃)\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#NDTensors.contract-Tuple{MPO, MPS}","page":"MPS and MPO","title":"NDTensors.contract","text":"contract(ψ::MPS, A::MPO; kwargs...) -> MPS\n*(::MPS, ::MPO; kwargs...) -> MPS\n\ncontract(A::MPO, ψ::MPS; kwargs...) -> MPS\n*(::MPO, ::MPS; kwargs...) -> MPS\n\nContract the MPO A with the MPS ψ, returning an MPS with the unique site indices of the MPO.\n\nFor example, for an MPO with site indices with prime levels of 1 and 0, such as -s'-A-s-, and an MPS with site indices with prime levels of 0, such as -s-x, the result is an MPS y with site indices with prime levels of 1, -s'-y = -s'-A-s-x.\n\nSince it is common to contract an MPO with prime levels of 1 and 0 with an MPS with prime level of 0 and want a resulting MPS with prime levels of 0, we provide a convenience function apply:\n\napply(A, x; kwargs...) = replaceprime(contract(A, x; kwargs...), 2 => 1)`.\n\nChoose the method with the method keyword, for example \"densitymatrix\" and \"naive\".\n\nKeywords\n\ncutoff::Float64=1e-13: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.\nmaxdim::Int=maxlinkdim(A) * maxlinkdim(ψ)): the maximal bond dimension of the results MPS.\nmindim::Int=1: the minimal bond dimension of the resulting MPS.\nnormalize::Bool=false: whether or not to normalize the resulting MPS.\nmethod::String=\"densitymatrix\": the algorithm to use for the contraction. Currently the options are \"densitymatrix\", where the network formed by the MPO and MPS is squared and contracted down to a density matrix which is diagonalized iteratively at each site, and \"naive\", where the MPO and MPS tensor are contracted exactly at each site and then a truncation of the resulting MPS is performed.\n\nSee also apply.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.apply-Tuple{MPO, MPS}","page":"MPS and MPO","title":"ITensors.apply","text":"apply(A::MPO, x::MPS; kwargs...)\n\nContract the MPO A with the MPS x and then map the prime level of the resulting MPS back to 0.\n\nEquivalent to replaceprime(contract(A, x; kwargs...), 2 => 1).\n\nSee also contract for details about the arguments available.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#NDTensors.contract-Tuple{MPO, MPO}","page":"MPS and MPO","title":"NDTensors.contract","text":"contract(A::MPO, B::MPO; kwargs...) -> MPO\n*(::MPO, ::MPO; kwargs...) -> MPO\n\nContract the MPO A with the MPO B, returning an MPO with the site indices that are not shared between A and B.\n\nIf you are contracting two MPOs with the same sets of indices, likely you want to call something like:\n\nC = contract(A', B; cutoff=1e-12)\nC = replaceprime(C, 2 => 1)\n\nThat is because if MPO A has the index structure -s'-A-s- and MPO B has the Index structure -s'-B-s-, if we only want to contract over on set of the indices, we would do (-s'-A-s-)'-s'-B-s- = -s''-A-s'-s'-B-s- = -s''-C-s-, and then map the prime levels back to pairs of primed and unprimed indices with: replaceprime(-s''-C-s-, 2 => 1) = -s'-C-s-.\n\nSince this is a common use case, you can use the convenience function:\n\nC = apply(A, B; cutoff=1e-12)\n\nwhich is the same as the code above.\n\nIf you are contracting MPOs that have diverging norms, such as MPOs representing sums of local operators, the truncation can become numerically unstable (see https://arxiv.org/abs/1909.06341 for a more numerically stable alternative). For now, you can use the following options to contract MPOs like that:\n\nC = contract(A, B; alg=\"naive\", truncate=false)\n# Bring the indices back to pairs of primed and unprimed\nC = apply(A, B; alg=\"naive\", truncate=false)\n\nKeywords\n\ncutoff::Float64=1e-14: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.\nmaxdim::Int=maxlinkdim(A) * maxlinkdim(B)): the maximal bond dimension of the results MPS.\nmindim::Int=1: the minimal bond dimension of the resulting MPS.\nalg=\"zipup\": Either \"zipup\" or \"naive\". \"zipup\" contracts pairs of site tensors and truncates with SVDs in a sweep across the sites, while \"naive\" first contracts pairs of tensor exactly and then truncates at the end if truncate=true.\ntruncate=true: Enable or disable truncation. If truncate=false, ignore other truncation parameters like cutoff and maxdim. This is most relevant for the \"naive\" version, if you just want to contract the tensors pairwise exactly. This can be useful if you are contracting MPOs that have diverging norms, such as MPOs originating from sums of local operators.\n\nSee also apply for details about the arguments available.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.apply-Tuple{MPO, MPO}","page":"MPS and MPO","title":"ITensors.apply","text":"apply(A::MPO, B::MPO; kwargs...)\n\nContract the MPO A' with the MPO B and then map the prime level of the resulting MPO back to having pairs of indices with prime levels of 1 and 0.\n\nEquivalent to replaceprime(contract(A', B; kwargs...), 2 => 1).\n\nSee also contract for details about the arguments available.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.error_contract-Tuple{MPS, MPO, MPS}","page":"MPS and MPO","title":"ITensors.ITensorMPS.error_contract","text":"error_contract(y::MPS, A::MPO, x::MPS;\n make_inds_match::Bool = true)\nerror_contract(y::MPS, x::MPS, x::MPO;\n make_inds_match::Bool = true)\n\nCompute the distance between A|x> and an approximation MPS y: | |y> - A|x> |/| A|x> | = √(1 + ( - 2*real())/).\n\nIf make_inds_match = true, the function attempts match the site indices of y with the site indices of A that are not common with x.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#NDTensors.outer-Tuple{MPS, MPS}","page":"MPS and MPO","title":"NDTensors.outer","text":"outer(x::MPS, y::MPS; ) -> MPO\n\nCompute the outer product of MPS x and MPS y, returning an MPO approximation. Note that y will be conjugated.\n\nIn Dirac notation, this is the operation |x⟩⟨y|.\n\nIf you want an outer product of an MPS with itself, you should call outer(x', x; kwargs...) so that the resulting MPO has site indices with indices coming in pairs of prime levels of 1 and 0. If not, the site indices won't be unique which would not be an outer product.\n\nFor example:\n\ns = siteinds(\"S=1/2\", 5)\nx = randomMPS(s)\ny = randomMPS(s)\nouter(x, y) # Incorrect! Site indices must be unique.\nouter(x', y) # Results in an MPO with pairs of primed and unprimed indices.\n\nThis allows for more general outer products, such as more general MPO outputs which don't have pairs of primed and unprimed indices, or outer products where the input MPS are vectorizations of MPOs.\n\nFor example:\n\ns = siteinds(\"S=1/2\", 5)\nX = MPO(s, \"Id\")\nY = MPO(s, \"Id\")\nx = convert(MPS, X)\ny = convert(MPS, Y)\nouter(x, y) # Incorrect! Site indices must be unique.\nouter(x', y) # Incorrect! Site indices must be unique.\nouter(addtags(x, \"Out\"), addtags(y, \"In\")) # This performs a proper outer product.\n\nThe keyword arguments determine the truncation, and accept the same arguments as contract(::MPO, ::MPO; kwargs...).\n\nSee also apply, contract.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.projector-Tuple{MPS}","page":"MPS and MPO","title":"ITensors.ITensorMPS.projector","text":"projector(x::MPS; ) -> MPO\n\nComputes the projector onto the state x. In Dirac notation, this is the operation |x⟩⟨x|/|⟨x|x⟩|².\n\nUse keyword arguments to control the level of truncation, which are the same as those accepted by contract(::MPO, ::MPO; kw...).\n\nKeywords\n\nnormalize::Bool=true: whether or not to normalize the input MPS before forming the projector. If normalize==false and the input MPS is not already normalized, this function will not output a proper project, and simply outputs outer(x, x) = |x⟩⟨x|, i.e. the projector scaled by norm(x)^2.\ntruncation keyword arguments accepted by contract(::MPO, ::MPO; kw...).\n\nSee also outer, contract.\n\n\n\n\n\n","category":"method"},{"location":"ProjMPO.html#ProjMPO","page":"ProjMPO","title":"ProjMPO","text":"","category":"section"},{"location":"ProjMPO.html#Description","page":"ProjMPO","title":"Description","text":"","category":"section"},{"location":"ProjMPO.html","page":"ProjMPO","title":"ProjMPO","text":"ProjMPO","category":"page"},{"location":"ProjMPO.html#ITensors.ITensorMPS.ProjMPO","page":"ProjMPO","title":"ITensors.ITensorMPS.ProjMPO","text":"A ProjMPO computes and stores the projection of an MPO into a basis defined by an MPS, leaving a certain number of site indices of the MPO unprojected. Which sites are unprojected can be shifted by calling the position! method.\n\nDrawing of the network represented by a ProjMPO P(H), showing the case of nsite(P)==2 and position!(P,psi,4) for an MPS psi:\n\no--o--o- -o--o--o--o--o--o \n\n\n\n\n\n","category":"type"},{"location":"ProjMPO.html#Methods","page":"ProjMPO","title":"Methods","text":"","category":"section"},{"location":"ProjMPO.html","page":"ProjMPO","title":"ProjMPO","text":"product(::ProjMPO,::ITensor)\nposition!(::ProjMPO, ::MPS, ::Int)\nnoiseterm(::ProjMPO,::ITensor,::String)","category":"page"},{"location":"ProjMPO.html#ITensors.product-Tuple{ProjMPO, ITensor}","page":"ProjMPO","title":"ITensors.product","text":"product(P::ProjMPO,v::ITensor)::ITensor\n\n(P::ProjMPO)(v::ITensor)\n\nEfficiently multiply the ProjMPO P by an ITensor v in the sense that the ProjMPO is a generalized square matrix or linear operator and v is a generalized vector in the space where it acts. The returned ITensor will have the same indices as v. The operator overload P(v) is shorthand for product(P,v).\n\n\n\n\n\n","category":"method"},{"location":"ProjMPO.html#ITensors.ITensorMPS.position!-Tuple{ProjMPO, MPS, Int64}","page":"ProjMPO","title":"ITensors.ITensorMPS.position!","text":"position!(P::ProjMPO, psi::MPS, pos::Int)\n\nGiven an MPS psi, shift the projection of the MPO represented by the ProjMPO P such that the set of unprojected sites begins with site pos. This operation efficiently reuses previous projections of the MPO on sites that have already been projected. The MPS psi must have compatible bond indices with the previous projected MPO tensors for this operation to succeed.\n\n\n\n\n\n","category":"method"},{"location":"ProjMPO.html#ITensors.ITensorMPS.noiseterm-Tuple{ProjMPO, ITensor, String}","page":"ProjMPO","title":"ITensors.ITensorMPS.noiseterm","text":"noiseterm(P::ProjMPO,\n phi::ITensor,\n ortho::String)\n\nReturn a \"noise term\" or density matrix perturbation ITensor as proposed in Phys. Rev. B 72, 180403 for aiding convergence of DMRG calculations. The ITensor phi is the contracted product of MPS tensors acted on by the ProjMPO P, and ortho is a String which can take the values \"left\" or \"right\" depending on the sweeping direction of the DMRG calculation.\n\n\n\n\n\n","category":"method"},{"location":"ProjMPO.html#Properties","page":"ProjMPO","title":"Properties","text":"","category":"section"},{"location":"ProjMPO.html","page":"ProjMPO","title":"ProjMPO","text":"length(::ProjMPO)\neltype(::ProjMPO)\nsize(::ProjMPO)","category":"page"},{"location":"ProjMPO.html#Base.length-Tuple{ProjMPO}","page":"ProjMPO","title":"Base.length","text":"length(P::ProjMPO)\n\nThe length of a ProjMPO is the same as the length of the MPO used to construct it\n\n\n\n\n\n","category":"method"},{"location":"ProjMPO.html#Base.eltype-Tuple{ProjMPO}","page":"ProjMPO","title":"Base.eltype","text":"eltype(P::ProjMPO)\n\nDeduce the element type (such as Float64 or ComplexF64) of the tensors in the ProjMPO P.\n\n\n\n\n\n","category":"method"},{"location":"ProjMPO.html#Base.size-Tuple{ProjMPO}","page":"ProjMPO","title":"Base.size","text":"size(P::ProjMPO)\n\nThe size of a ProjMPO are its dimensions (d,d) when viewed as a matrix or linear operator acting on a space of dimension d.\n\nFor example, if a ProjMPO maps from a space with indices (a,s1,s2,b) to the space (a',s1',s2',b') then the size is (d,d) where d = dim(a)*dim(s1)*dim(s1)*dim(b)\n\n\n\n\n\n","category":"method"},{"location":"faq/Development.html#ITensor-Development-Frequently-Asked-Questions","page":"ITensor Development FAQs","title":"ITensor Development Frequently Asked Questions","text":"","category":"section"},{"location":"faq/Development.html#What-are-the-steps-to-contribute-code-to-ITensor?","page":"ITensor Development FAQs","title":"What are the steps to contribute code to ITensor?","text":"","category":"section"},{"location":"faq/Development.html","page":"ITensor Development FAQs","title":"ITensor Development FAQs","text":"Please contact us (support at itensor.org) if you are planning to submit a major contribution (more than a few lines of code, say). If so, we would like to discuss your plan and design before you spend significant time on it, to increase the chances we will merge your pull request.\nFork the ITensors.jl Github repo, create a new branch and make changes (commits) on that branch. ITensor imposes code formatting for contributions. Please run using JuliaFormatter; format(\".\") in the project directory to ensure formatting. As an alternative you may also use pre-commit. Install pre-commit with e.g. pip install pre-commit, then run pre-commit install in the project directory in order for pre-commit to run automatically before any commit.\nRun the ITensor unit tests by going into the test/ folder and running julia runtests.jl. To run individual test scripts, start a Julia REPL (interactive terminal) session and include each script, such as include(\"itensor.jl\").\nPush your new branch and changes to your forked repo. Github will give you the option to make a pull request (PR) out of your branch that will be submitted to us, and which you can view under the list of ITensors.jl pull requests. If your PR's tests pass and we approve your changes, we will merge it or ask you to merge it. If you merge your PR, please use the Squash and Merge option. We may also ask you to make more changes to bring your PR in line with our design goals or technical requirements.","category":"page"},{"location":"IndexSetType.html#Index-collections","page":"Index collections","title":"Index collections","text":"","category":"section"},{"location":"IndexSetType.html","page":"Index collections","title":"Index collections","text":"Collections of Index are used throughout ITensors.jl to represent the dimensions of tensors. In general, collections that are recognized and returned by ITensors.jl functions are either Vector of Index or Tuple of Index, depending on the context. For example internally an ITensor has a static number of indices so stores a Tuple of Index, while set operations like commoninds((i, j, k), (j, k, l)) will return a Vector [j, k] since the operation is inherently dynamic, i.e. the number of indices in the intersection can't in general be known before running the code. Vector of Index and Tuple of Index can usually be used interchangeably, but one or the other may be faster depending on the operation being performed.","category":"page"},{"location":"IndexSetType.html#Priming_and_tagging_IndexSet","page":"Index collections","title":"Priming and tagging","text":"","category":"section"},{"location":"IndexSetType.html","page":"Index collections","title":"Index collections","text":"Documentation for priming and tagging collections of Index can be found in the ITensor Priming and tagging section.","category":"page"},{"location":"IndexSetType.html#Set-operations","page":"Index collections","title":"Set operations","text":"","category":"section"},{"location":"IndexSetType.html","page":"Index collections","title":"Index collections","text":"Documentation for set operations involving Index collections can be found in the ITensor Index collections set operations section.","category":"page"},{"location":"IndexSetType.html#Subsets","page":"Index collections","title":"Subsets","text":"","category":"section"},{"location":"IndexSetType.html","page":"Index collections","title":"Index collections","text":"getfirst(::Function, ::IndexSet)\ngetfirst(::IndexSet)","category":"page"},{"location":"IndexSetType.html#ITensors.getfirst-Tuple{Function, Vector{IndexT} where IndexT<:Index}","page":"Index collections","title":"ITensors.getfirst","text":"getfirst(f::Function, is::Indices)\n\nGet the first Index matching the pattern function, return nothing if not found.\n\n\n\n\n\n","category":"method"},{"location":"IndexSetType.html#ITensors.getfirst-Tuple{Vector{IndexT} where IndexT<:Index}","page":"Index collections","title":"ITensors.getfirst","text":"getfirst(is::Indices)\n\nReturn the first Index in the Indices. If the Indices is empty, return nothing.\n\n\n\n\n\n","category":"method"},{"location":"IndexSetType.html#Iterating","page":"Index collections","title":"Iterating","text":"","category":"section"},{"location":"IndexSetType.html","page":"Index collections","title":"Index collections","text":"eachval(::Index...)\neachindval(::Index...)","category":"page"},{"location":"IndexSetType.html#ITensors.eachval-Tuple{Vararg{Index}}","page":"Index collections","title":"ITensors.eachval","text":"eachval(is::Index...)\neachval(is::Tuple{Vararg{Index}})\n\nCreate an iterator whose values correspond to a Cartesian indexing over the dimensions of the provided Index objects.\n\n\n\n\n\n","category":"method"},{"location":"IndexSetType.html#ITensors.eachindval-Tuple{Vararg{Index}}","page":"Index collections","title":"ITensors.eachindval","text":"eachindval(is::Index...)\neachindval(is::Tuple{Vararg{Index}})\n\nCreate an iterator whose values are Index=>value pairs corresponding to a Cartesian indexing over the dimensions of the provided Index objects.\n\nExample\n\ni = Index(3; tags=\"i\")\nj = Index(2; tags=\"j\")\nT = randomITensor(j, i)\nfor iv in eachindval(i, j)\n @show T[iv...]\nend\n\n\n\n\n\n","category":"method"},{"location":"IndexSetType.html#Symmetry-related-properties","page":"Index collections","title":"Symmetry related properties","text":"","category":"section"},{"location":"IndexSetType.html","page":"Index collections","title":"Index collections","text":"dir(::IndexSet, ::Index)","category":"page"},{"location":"IndexSetType.html#ITensors.dir-Tuple{Vector{IndexT} where IndexT<:Index, Index}","page":"Index collections","title":"ITensors.dir","text":"dir(is::Indices, i::Index)\n\nReturn the direction of the Index i in the Indices is.\n\n\n\n\n\n","category":"method"},{"location":"ContractionSequenceOptimization.html#Contraction-sequence-optimization","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"","category":"section"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"When contracting a tensor network, the sequence of contraction makes a big difference in the computational cost. However, the complexity of determining the optimal sequence grows exponentially with the number of tensors, but there are many heuristic algorithms available for computing optimal sequences for small networks[1][2][3][4][5][6]. ITensors.jl provides some functionality for helping you find the optimal contraction sequence for small tensor network, as we will show below.","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"The algorithm in ITensors.jl currently uses a modified version of[1] with simplifications for outer product contractions similar to those used in TensorOperations.jl.","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"[1]: Faster identification of optimal contraction sequences for tensor networks","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"[2]: Improving the efficiency of variational tensor network algorithms","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"[3]: Simulating quantum computation by contracting tensor networks","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"[4]: Towards a polynomial algorithm for optimal contraction sequence of tensor networks from trees","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"[5]: Algorithms for Tensor Network Contraction Ordering","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"[6]: Hyper-optimized tensor network contraction","category":"page"},{"location":"ContractionSequenceOptimization.html#Functions","page":"Contraction sequence optimization","title":"Functions","text":"","category":"section"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"ITensors.optimal_contraction_sequence\nITensors.contraction_cost\ncontract","category":"page"},{"location":"ContractionSequenceOptimization.html#ITensors.ContractionSequenceOptimization.optimal_contraction_sequence","page":"Contraction sequence optimization","title":"ITensors.ContractionSequenceOptimization.optimal_contraction_sequence","text":"optimal_contraction_sequence(T)\n\nReturns a contraction sequence for contracting the tensors T. The sequence is generally optimal (currently, outer product contractions are skipped, but some optimal sequences require outer product contractions).\n\n\n\n\n\n","category":"function"},{"location":"ContractionSequenceOptimization.html#ITensors.ContractionSequenceOptimization.contraction_cost","page":"Contraction sequence optimization","title":"ITensors.ContractionSequenceOptimization.contraction_cost","text":"contraction_cost(A; sequence)\n\nReturn the cost of contracting the collection of ITensors according to the specified sequence, where the cost is measured in the number of floating point operations that would need to be performed to contract dense tensors of the dimensions specified by the indices of the tensors (so for now, sparsity is ignored in computing the costs). Pairwise costs are returned in a vector (contracting N tensors requires N-1 pairwise contractions). You can use sum(contraction_cost(A; sequence)) to get the total cost of the contraction.\n\nIf no sequence is specified, left associative contraction is used, in other words the sequence is equivalent to [[[[1, 2], 3], 4], …].\n\n\n\n\n\n","category":"function"},{"location":"ContractionSequenceOptimization.html#NDTensors.contract","page":"Contraction sequence optimization","title":"NDTensors.contract","text":"contract(ψ::MPS, A::MPO; kwargs...) -> MPS\n*(::MPS, ::MPO; kwargs...) -> MPS\n\ncontract(A::MPO, ψ::MPS; kwargs...) -> MPS\n*(::MPO, ::MPS; kwargs...) -> MPS\n\nContract the MPO A with the MPS ψ, returning an MPS with the unique site indices of the MPO.\n\nFor example, for an MPO with site indices with prime levels of 1 and 0, such as -s'-A-s-, and an MPS with site indices with prime levels of 0, such as -s-x, the result is an MPS y with site indices with prime levels of 1, -s'-y = -s'-A-s-x.\n\nSince it is common to contract an MPO with prime levels of 1 and 0 with an MPS with prime level of 0 and want a resulting MPS with prime levels of 0, we provide a convenience function apply:\n\napply(A, x; kwargs...) = replaceprime(contract(A, x; kwargs...), 2 => 1)`.\n\nChoose the method with the method keyword, for example \"densitymatrix\" and \"naive\".\n\nKeywords\n\ncutoff::Float64=1e-13: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.\nmaxdim::Int=maxlinkdim(A) * maxlinkdim(ψ)): the maximal bond dimension of the results MPS.\nmindim::Int=1: the minimal bond dimension of the resulting MPS.\nnormalize::Bool=false: whether or not to normalize the resulting MPS.\nmethod::String=\"densitymatrix\": the algorithm to use for the contraction. Currently the options are \"densitymatrix\", where the network formed by the MPO and MPS is squared and contracted down to a density matrix which is diagonalized iteratively at each site, and \"naive\", where the MPO and MPS tensor are contracted exactly at each site and then a truncation of the resulting MPS is performed.\n\nSee also apply.\n\n\n\n\n\ncontract(A::MPO, B::MPO; kwargs...) -> MPO\n*(::MPO, ::MPO; kwargs...) -> MPO\n\nContract the MPO A with the MPO B, returning an MPO with the site indices that are not shared between A and B.\n\nIf you are contracting two MPOs with the same sets of indices, likely you want to call something like:\n\nC = contract(A', B; cutoff=1e-12)\nC = replaceprime(C, 2 => 1)\n\nThat is because if MPO A has the index structure -s'-A-s- and MPO B has the Index structure -s'-B-s-, if we only want to contract over on set of the indices, we would do (-s'-A-s-)'-s'-B-s- = -s''-A-s'-s'-B-s- = -s''-C-s-, and then map the prime levels back to pairs of primed and unprimed indices with: replaceprime(-s''-C-s-, 2 => 1) = -s'-C-s-.\n\nSince this is a common use case, you can use the convenience function:\n\nC = apply(A, B; cutoff=1e-12)\n\nwhich is the same as the code above.\n\nIf you are contracting MPOs that have diverging norms, such as MPOs representing sums of local operators, the truncation can become numerically unstable (see https://arxiv.org/abs/1909.06341 for a more numerically stable alternative). For now, you can use the following options to contract MPOs like that:\n\nC = contract(A, B; alg=\"naive\", truncate=false)\n# Bring the indices back to pairs of primed and unprimed\nC = apply(A, B; alg=\"naive\", truncate=false)\n\nKeywords\n\ncutoff::Float64=1e-14: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.\nmaxdim::Int=maxlinkdim(A) * maxlinkdim(B)): the maximal bond dimension of the results MPS.\nmindim::Int=1: the minimal bond dimension of the resulting MPS.\nalg=\"zipup\": Either \"zipup\" or \"naive\". \"zipup\" contracts pairs of site tensors and truncates with SVDs in a sweep across the sites, while \"naive\" first contracts pairs of tensor exactly and then truncates at the end if truncate=true.\ntruncate=true: Enable or disable truncation. If truncate=false, ignore other truncation parameters like cutoff and maxdim. This is most relevant for the \"naive\" version, if you just want to contract the tensors pairwise exactly. This can be useful if you are contracting MPOs that have diverging norms, such as MPOs originating from sums of local operators.\n\nSee also apply for details about the arguments available.\n\n\n\n\n\n*(As::ITensor...; sequence = default_sequence(), kwargs...)\n*(As::Vector{<: ITensor}; sequence = default_sequence(), kwargs...)\ncontract(As::ITensor...; sequence = default_sequence(), kwargs...)\n\nContract the set of ITensors according to the contraction sequence.\n\nThe default sequence is \"automatic\" if ITensors.using_contraction_sequence_optimization() is true, otherwise it is \"left_associative\" (the ITensors are contracted from left to right).\n\nYou can change the default with ITensors.enable_contraction_sequence_optimization() and ITensors.disable_contraction_sequence_optimization().\n\nFor a custom sequence, the sequence should be provided as a binary tree where the leaves are integers n specifying the ITensor As[n] and branches are accessed by indexing with 1 or 2, i.e. sequence = Any[Any[1, 3], Any[2, 4]].\n\n\n\n\n\n","category":"function"},{"location":"ContractionSequenceOptimization.html#Examples","page":"Contraction sequence optimization","title":"Examples","text":"","category":"section"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"In the following example we show how to compute the contraction sequence cost of a","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"using ITensors\nusing Symbolics\n\nusing ITensors: contraction_cost\n\n@variables m, k, d\n\nl = Index(m, \"l\")\nr = Index(m, \"r\")\nh₁ = Index(k, \"h₁\")\nh₂ = Index(k, \"h₂\")\nh₃ = Index(k, \"h₃\")\ns₁ = Index(d, \"s₁\")\ns₂ = Index(d, \"s₂\")\n\nH₁ = emptyITensor(dag(s₁), s₁', dag(h₁), h₂)\nH₂ = emptyITensor(dag(s₂), s₂', dag(h₂), h₃)\nL = emptyITensor(dag(l), l', h₁)\nR = emptyITensor(dag(r), r', h₃)\nψ = emptyITensor(l, s₁, s₂, r)\n\nTN = [ψ, L, H₁, H₂, R]\nsequence1 = Any[2, Any[3, Any[4, Any[1, 5]]]]\nsequence2 = Any[Any[4, 5], Any[1, Any[2, 3]]]\ncost1 = contraction_cost(TN; sequence = sequence1)\ncost2 = contraction_cost(TN; sequence = sequence2)\n\nprintln(\"First sequence\")\ndisplay(sequence1)\ndisplay(cost1)\n@show sum(cost1)\n@show substitute(sum(cost1), Dict(d => 4))\n\nprintln(\"\\nSecond sequence\")\ndisplay(sequence2)\ndisplay(cost2)\n@show sum(cost2)\n@show substitute(sum(cost2), Dict(d => 4))","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"This example helps us learn that in the limit of large MPS bond dimension m, the first contraction sequence is faster, while in the limit of large MPO bond dimension k, the second sequence is faster. This has practical implications for writing an efficient DMRG algorithm in both limits, which we plan to incorporate into ITensors.jl.","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"Here is a more systematic example of searching through the parameter space to find optimal contraction sequences:","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"using ITensors\nusing Symbolics\n\nusing ITensors: contraction_cost, optimal_contraction_sequence\n\nfunction tensor_network(; m, k, d)\n l = Index(m, \"l\")\n r = Index(m, \"r\")\n h₁ = Index(k, \"h₁\")\n h₂ = Index(k, \"h₂\")\n h₃ = Index(k, \"h₃\")\n s₁ = Index(d, \"s₁\")\n s₂ = Index(d, \"s₂\")\n\n ψ = emptyITensor(l, s₁, s₂, r)\n L = emptyITensor(dag(l), l', h₁)\n H₁ = emptyITensor(dag(s₁), s₁', dag(h₁), h₂)\n H₂ = emptyITensor(dag(s₂), s₂', dag(h₂), h₃)\n R = emptyITensor(dag(r), r', h₃)\n return [ψ, L, H₁, H₂, R]\nend\n\nfunction main()\n mrange = 50:10:80\n krange = 50:10:80\n sequence_costs = Matrix{Any}(undef, length(mrange), length(krange))\n for iₘ in eachindex(mrange), iₖ in eachindex(krange)\n m_val = mrange[iₘ]\n k_val = krange[iₖ]\n d_val = 4\n\n TN = tensor_network(; m = m_val, k = k_val, d = d_val)\n sequence = optimal_contraction_sequence(TN)\n cost = contraction_cost(TN; sequence = sequence)\n\n @variables m, k, d\n TN_symbolic = tensor_network(; m = m, k = k, d = d)\n cost_symbolic = contraction_cost(TN_symbolic; sequence = sequence)\n sequence_cost = (dims = (m = m_val, k = k_val, d = d_val), sequence = sequence, cost = cost, symbolic_cost = cost_symbolic)\n sequence_costs[iₘ, iₖ] = sequence_cost\n end\n return sequence_costs\nend\n\nsequence_costs = main()\n\n# Analyze the results.\nprintln(\"Index dimensions\")\ndisplay(getindex.(sequence_costs, :dims))\n\nprintln(\"\\nContraction sequences\")\ndisplay(getindex.(sequence_costs, :sequence))\n\nprintln(\"\\nSymbolic contraction cost with d = 4\")\n# Fix d to a certain value (such as 4 for a Hubbard site)\n@variables d\nvar_sub = Dict(d => 4)\ndisplay(substitute.(sum.(getindex.(sequence_costs, :symbolic_cost)), (var_sub,)))","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"A future direction will be to allow optimizing over contraction sequences with the dimensions specified symbolically, so that the optimal sequence in limits of certain dimensions can be found. In addition, we plan to implement more algorithms that work for larger networks, as well as algorithms like[2] which take an optimal sequence for a closed network and generate optimal sequences for environments of each tensor in the network, which is helpful for computing gradients of tensor networks.","category":"page"}] +[{"location":"DMRGObserver.html#DMRGObserver","page":"DMRGObserver","title":"DMRGObserver","text":"","category":"section"},{"location":"DMRGObserver.html","page":"DMRGObserver","title":"DMRGObserver","text":"A DMRGObserver is a type of observer which offers certain useful, general purpose capabilities for DMRG calculations such as measuring custom local observables at each step and stopping DMRG early if certain energy convergence conditions are met.","category":"page"},{"location":"DMRGObserver.html#Sample-Usage","page":"DMRGObserver","title":"Sample Usage","text":"","category":"section"},{"location":"DMRGObserver.html","page":"DMRGObserver","title":"DMRGObserver","text":"In the following example, we have already made a Hamiltonian MPO H and initial MPS psi0 for a system of spins whose sites have an associated \"Sz\" operator defined. We construct a DMRGObserver which measures \"Sz\" on each site at each step of DMRG, and also stops the calculation early if the energy no longer changes to a relative precision of 1E-7.","category":"page"},{"location":"DMRGObserver.html","page":"DMRGObserver","title":"DMRGObserver","text":"Sz_observer = DMRGObserver([\"Sz\"],sites,energy_tol=1E-7)\n\nenergy, psi = dmrg(H,psi0,sweeps,observer=Sz_observer)\n\nfor (sw,Szs) in enumerate(measurements(Sz_observer)[\"Sz\"])\n println(\"Total Sz after sweep $sw = \", sum(Szs)/N)\nend","category":"page"},{"location":"DMRGObserver.html#Constructors","page":"DMRGObserver","title":"Constructors","text":"","category":"section"},{"location":"DMRGObserver.html","page":"DMRGObserver","title":"DMRGObserver","text":"DMRGObserver(;energy_tol::Float64,minsweeps::Int)\nDMRGObserver(ops::Vector{String},sites::Vector{<:Index};energy_tol::Float64,minsweeps::Int)","category":"page"},{"location":"DMRGObserver.html#ITensors.ITensorMPS.DMRGObserver-Tuple{}","page":"DMRGObserver","title":"ITensors.ITensorMPS.DMRGObserver","text":"DMRGObserver(;energy_tol=0.0,\n minsweeps=2,\n energy_type=Float64)\n\nConstruct a DMRGObserver by providing the energy tolerance used for early stopping, and minimum number of sweeps that must be done.\n\nOptional keyword arguments:\n\nenergy_tol: if the energy from one sweep to the next no longer changes by more than this amount, stop after the current sweep\nminsweeps: do at least this many sweeps\nenergy_type: type to use when storing energies at each step\n\n\n\n\n\n","category":"method"},{"location":"DMRGObserver.html#ITensors.ITensorMPS.DMRGObserver-Tuple{Vector{String}, Vector{<:Index}}","page":"DMRGObserver","title":"ITensors.ITensorMPS.DMRGObserver","text":"DMRGObserver(ops::Vector{String},\n sites::Vector{<:Index};\n energy_tol=0.0,\n minsweeps=2,\n energy_type=Float64)\n\nConstruct a DMRGObserver, provide an array of ops of operator names which are strings recognized by the op function. Each of these operators will be measured on every site during every step of DMRG and the results recorded inside the DMRGOberver for later analysis. The array sites is the basis of sites used to define the MPS and MPO for the DMRG calculation.\n\nOptionally, one can provide an energy tolerance used for early stopping, and minimum number of sweeps that must be done.\n\nOptional keyword arguments:\n\nenergy_tol: if the energy from one sweep to the next no longer changes by more than this amount, stop after the current sweep\nminsweeps: do at least this many sweeps\nenergy_type: type to use when storing energies at each step\n\n\n\n\n\n","category":"method"},{"location":"DMRGObserver.html#Methods","page":"DMRGObserver","title":"Methods","text":"","category":"section"},{"location":"DMRGObserver.html","page":"DMRGObserver","title":"DMRGObserver","text":"measurements(::DMRGObserver)\nDMRGMeasurement\nenergies(::DMRGObserver)","category":"page"},{"location":"DMRGObserver.html#ITensors.ITensorMPS.measurements-Tuple{DMRGObserver}","page":"DMRGObserver","title":"ITensors.ITensorMPS.measurements","text":"measurements(o::DMRGObserver)\n\nAfter using a DMRGObserver object o within a DMRG calculation, retrieve a dictionary of measurement results, with the keys being operator names and values being DMRGMeasurement objects.\n\n\n\n\n\n","category":"method"},{"location":"DMRGObserver.html#ITensors.ITensorMPS.DMRGMeasurement","page":"DMRGObserver","title":"ITensors.ITensorMPS.DMRGMeasurement","text":"A DMRGMeasurement object is an alias for Vector{Vector{Float64}}, in other words an array of arrays of real numbers.\n\nGiven a DMRGMeasurement M,the result for the measurement on sweep n and site i as M[n][i].\n\n\n\n\n\n","category":"type"},{"location":"DMRGObserver.html#ITensors.ITensorMPS.energies-Tuple{DMRGObserver}","page":"DMRGObserver","title":"ITensors.ITensorMPS.energies","text":"energies(o::DMRGObserver)\n\nAfter using a DMRGObserver object o within a DMRG calculation, retrieve an array of the energy after each sweep.\n\n\n\n\n\n","category":"method"},{"location":"HDF5FileFormats.html#HDF5-File-Formats","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"This page lists the formats for the HDF5 representations of various types in the ITensors module.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 is a portable file format which has a directory structure similar to a file system. In addition to containing \"groups\" (= directories) and \"datasets\" (= files), groups can have \"attributes\" appended to them, which are similar to 'tags' or 'keywords'. Unless otherwise specified, integers are 64 bit and are signed (H5T_STD_I64LE) unless explicitly stated. (For example, the \"id\" field of the Index type is stored as an unsigned 64 bit integer (H5T_STD_U64LE).)","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Each type in ITensor which is writeable to HDF5 is written to its own group, with the name of the group either specified by the user or specified to some default value when it is a subgroup of another ITensor type (for example, the Index type saves its TagSet in a subgroup named \"tags\").","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Each group corresponding to an ITensors type always carries the following attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"type\" –- a string such as Index or TagSet specifying the information necessary to determine the type of the object saved to the HDF5 group\n\"version\" –- an integer specifying the file format version used to store the data. This version is in general different from the release version of ITensors.jl. The purpose of the version number is to aid in maintaining backwards compatibility, while allowing the format to be occasionally changed.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"The C++ version of ITensor uses exactly the same file formats listed below, for the purpose of interoperability with the Julia version of ITensor, even though conventions such as the \"type\" field values are Julia-centric.","category":"page"},{"location":"HDF5FileFormats.html#tagset_hdf5","page":"HDF5 File Formats","title":"TagSet","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for the ITensors.TagSet type.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"TagSet\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"tags\" [string] = a comma separated string of the tags in the TagSet","category":"page"},{"location":"HDF5FileFormats.html#qn_hdf5","page":"HDF5 File Formats","title":"QN","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for the ITensors.QN type.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"QN\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"names\" [group] = array of strings (length 4) of names of quantum numbers\n\"vals\" [group] = array of integers (length 4) of quantum number values\n\"mods\" [group] = array of integers (length 4) of moduli of quantum numbers","category":"page"},{"location":"HDF5FileFormats.html#qnblocks_hdf5","page":"HDF5 File Formats","title":"QNBlocks","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for the ITensors.QNBlocks type. (Note: QNBlocks is equivalent to Vector{Pair{QN, Int64}}.)","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"QNBlocks\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"length\" [integer] = the number of blocks (length of Vector)\n\"dims\" [group] = array of (integer) dimensions of each block\n\"QN[n]\" [group] = these groups \"QN[1]\", \"QN[2]\", etc. correspond to the QN of each block","category":"page"},{"location":"HDF5FileFormats.html#index_hdf5","page":"HDF5 File Formats","title":"Index","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for the ITensors.Index type.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"Index\"\n\"space_type\" = \"Int\" if the Index is a regular, dense Index or \"QNBlocks\" if the Index is a QNIndex (carries QN subspace information)","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"id\" [unsigned integer] = id number of the Index\n\"dim\" [integer] = dimension of the Index\n\"dir\" [integer] = arrow direction of the Index, +1 for ITensors.Out and -1 for ITensors.In\n\"plev\" [integer] = prime level of the Index\n\"tags\" [group] = the TagSet of the Index","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Optional Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"space\" [group] = if the \"space_type\" attribute is \"QNBlocks\", this group is present and represents a QNBlocks object","category":"page"},{"location":"HDF5FileFormats.html#indexset_hdf5","page":"HDF5 File Formats","title":"IndexSet","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for types in the Union type ITensors.Indices which includes IndexSet and tuples of Index objects.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"IndexSet\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"length\" [integer] = number of indices\n\"index_n\" [group] = for n=1 to n=length each of these groups contains an Index","category":"page"},{"location":"HDF5FileFormats.html#itensor_hdf5","page":"HDF5 File Formats","title":"ITensor","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for the ITensors.ITensor type.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"ITensor\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"inds\" [group] = indices of the ITensor\n\"storage\" [group] = storage of the ITensor (note that some earlier versions of ITensors.jl may call this group \"store\")","category":"page"},{"location":"HDF5FileFormats.html#dense_hdf5","page":"HDF5 File Formats","title":"NDTensors.Dense","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for objects which are subtypes of ITensors.NDTensors.Dense.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"Dense{Float64}\" or \"Dense{ComplexF64}\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"data\" = array of either real or complex values (in the same dataset format used by the HDF5.jl library for storing Vector{Float64} or Vector{ComplexF64})","category":"page"},{"location":"HDF5FileFormats.html#blocksparse_hdf5","page":"HDF5 File Formats","title":"NDTensors.BlockSparse","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for objects which are subtypes of ITensors.NDTensors.BlockSparse.","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"BlockSparse{Float64}\" or \"BlockSparse{ComplexF64}\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"ndims\" [integer] = number of dimensions (order) of the tensor\n\"offsets\" = block offset data flattened into an array of integers\n\"data\" = array of either real or complex values (in the same dataset format used by the HDF5.jl library for storing Vector{Float64} or Vector{ComplexF64})","category":"page"},{"location":"HDF5FileFormats.html#mps_hdf5","page":"HDF5 File Formats","title":"MPS","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for ITensors.MPS","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"MPS\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"length\" [integer] = number of tensors of the MPS\n\"rlim\" [integer] = right orthogonality limit\n\"llim\" [integer] = left orthogonality limit\n\"MPS[n]\" [group,ITensor] = each of these groups, where n=1,...,length, stores the nth ITensor of the MPS","category":"page"},{"location":"HDF5FileFormats.html#mpo_hdf5","page":"HDF5 File Formats","title":"MPO","text":"","category":"section"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"HDF5 file format for ITensors.MPO","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Attributes:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"version\" = 1\n\"type\" = \"MPO\"","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"Datasets and Subgroups:","category":"page"},{"location":"HDF5FileFormats.html","page":"HDF5 File Formats","title":"HDF5 File Formats","text":"\"length\" [integer] = number of tensors of the MPO\n\"rlim\" [integer] = right orthogonality limit\n\"llim\" [integer] = left orthogonality limit\n\"MPO[n]\" [group,ITensor] = each of these groups, where n=1,...,length, stores the nth ITensor of the MPO","category":"page"},{"location":"tutorials/MPSTimeEvolution.html#MPS-Time-Evolution","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"","category":"section"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"An important application of matrix product state (MPS) tensor networks in physics is computing the time evolution of a quantum state under the dynamics of a Hamiltonian H. An accurate, efficient, and simple way to time evolve a matrix product state (MPS) is by using a Trotter decomposition of the time evolution operator U(t) = e^-i H t.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"The technique we will use is \"time evolving block decimation\" (TEBD). More simply it is just the idea of decomposing the time-evolution operator into a circuit of quantum 'gates' (two-site unitaries) using the Trotter-Suzuki approximation and applying these gates in a controlled way to an MPS.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Let's see how to set up and run a TEBD calculation using ITensor.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"The Hamiltonian H we will use is the one-dimensional Heisenberg model which is given by:","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"beginaligned\nH = sum_j=1^N-1 mathbfS_j cdot mathbfS_j+1 \n = sum_j=1^N-1 S^z_j S^z_j+1 + frac12 S^+_j S^-_j+1 + frac12 S^-_j S^+_j+1\nendaligned","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"The TEBD Method","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"When the Hamiltonian, like the one above, is a sum of local terms,","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"H = sum_j h_jj+1","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"where h_jj+1 acts on sites j and j+1, then a Trotter decomposition that is particularly well suited for use with MPS techniques is","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"e^-i tau H approx e^-i h_12 tau2 e^-i h_23 tau2 cdots e^-i h_N-1N tau2\ne^-i h_N-1N tau2 e^-i h_N-2N-1 tau2 cdots e^-i h_12 tau2 + O(tau^3)","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Note the factors of two in each exponential. Each factored exponential is known as a Trotter \"gate\".","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"We can visualize the resulting circuit that will be applied to the MPS as follows:","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"(Image: )","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"The error in the above decomposition is of order tau^3, so that will be the error accumulated per time step. Because of the time-step error, one takes tau to be small and then applies the above set of operators to an MPS as a single sweep, then does a number (ttau) of sweeps to evolve for a total time t. The total error will therefore scale as tau^2 with this scheme, though other sources of error may dominate for long times, or very small tau, such as truncation errors.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Let's take a look at the code to apply these Trotter gates to an MPS to time evolve it. Then we will break down the steps of the code in more detail.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"ITensor TEBD Time Evolution Code","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Let's look at an entire, working ITensor code that will do this calculation then discuss the main steps. (If you need help running the code below, see the getting started page on running ITensor codes.)","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"using ITensors\n\nlet\n N = 100\n cutoff = 1E-8\n tau = 0.1\n ttotal = 5.0\n\n # Make an array of 'site' indices\n s = siteinds(\"S=1/2\", N; conserve_qns=true)\n\n # Make gates (1,2),(2,3),(3,4),...\n gates = ITensor[]\n for j in 1:(N - 1)\n s1 = s[j]\n s2 = s[j + 1]\n hj =\n op(\"Sz\", s1) * op(\"Sz\", s2) +\n 1 / 2 * op(\"S+\", s1) * op(\"S-\", s2) +\n 1 / 2 * op(\"S-\", s1) * op(\"S+\", s2)\n Gj = exp(-im * tau / 2 * hj)\n push!(gates, Gj)\n end\n # Include gates in reverse order too\n # (N,N-1),(N-1,N-2),...\n append!(gates, reverse(gates))\n\n # Initialize psi to be a product state (alternating up and down)\n psi = MPS(s, n -> isodd(n) ? \"Up\" : \"Dn\")\n\n c = div(N, 2) # center site\n\n # Compute and print at each time step\n # then apply the gates to go to the next time\n for t in 0.0:tau:ttotal\n Sz = expect(psi, \"Sz\"; sites=c)\n println(\"$t $Sz\")\n\n t≈ttotal && break\n\n psi = apply(gates, psi; cutoff)\n normalize!(psi)\n end\n\n return\nend","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Steps of The Code","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"First we setsome parameters, like the system size N and time step tau to use.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"The line s = siteinds(\"S=1/2\",N;conserve_qns=true) defines an array of spin 1/2 tensor indices (Index objects) which will be the site or physical indices of the MPS.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Next we make an empty array gates = ITensor[] that will hold ITensors that will be our Trotter gates. Inside the for n=1:N-1 loop that follows the lines","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"hj = op(\"Sz\",s1) * op(\"Sz\",s2) +\n 1/2 * op(\"S+\",s1) * op(\"S-\",s2) +\n 1/2 * op(\"S-\",s1) * op(\"S+\",s2)","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"call the op function which reads the \"S=1/2\" tag on our site indices (sites j and j+1) and which then knows that we want the spin 1/ 2 version of the \"Sz\", \"S+\", and \"S-\" operators. The op function returns these operators as ITensors and we tensor product and add them together to compute the operator h_jj+1 defined as","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"h_jj+1 = S^z_j S^z_j+1 + frac12 S^+_j S^-_j+1 + frac12 S^-_j S^+_j+1","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"which we call hj in the code.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"To make the corresponding Trotter gate Gj we exponentiate hj times a factor -i tau2 and then append or push this onto the end of the gate array gates.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Gj = exp(-im * tau/2 * hj)\npush!(gates,Gj)","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Having made the gates for bonds (1,2),(2,3),(3,4), etc. we still need to append the gates in reverse order to complete the correct Trotter formula. Here we can conveniently do that by just calling the Julia append! function and supply a reversed version of the array of gates we have made so far. This can be done in a single line of code append!(gates,reverse(gates)).","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"The line of code psi = MPS(s, n -> isodd(n) ? \"Up\" : \"Dn\") initializes our MPS psi as a product state of alternating up and down spins.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"To carry out the time evolution we loop over the range of times from 0.0 to ttotal in steps of tau, using the Julia range notation 0.0:tau:ttotal to easily set up this loop as for t in 0.0:tau:ttotal.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"Inside the loop, we use the expect function to measure the expected value of the \"Sz\" operator on the center site.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"To evolve the MPS to the next time, we call the function","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"psi = apply(gates, psi; cutoff)","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"which applies the array of ITensors called gates to our current MPS psi, truncating the MPS at each step using the truncation error threshold supplied as the variable cutoff.","category":"page"},{"location":"tutorials/MPSTimeEvolution.html","page":"MPS Time Evolution","title":"MPS Time Evolution","text":"The apply function is smart enough to determine which site indices each gate has, and then figure out where to apply it to our MPS. It automatically handles truncating the MPS and can even handle non-nearest-neighbor gates, though that feature is not used in this example.","category":"page"},{"location":"faq/JuliaAndCpp.html#Programming-Language-(Julia,-C)-Frequently-Asked-Questions","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++) Frequently Asked Questions","text":"","category":"section"},{"location":"faq/JuliaAndCpp.html#Should-I-use-the-Julia-or-C-version-of-ITensor?","page":"Programming Language (Julia, C++, ...) FAQs","title":"Should I use the Julia or C++ version of ITensor?","text":"","category":"section"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"We recommend the Julia version of ITensor for most people, because:","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Julia ITensor has more and newer features than C++ ITensor, and we are developing it more rapidly\nJulia is a more productive language than C++ with more built-in features, such as linear algebra, iteration tools, etc.\nJulia is a compiled language with performance rivaling C++ (see next question below for a longer discussion)\nJulia has a rich ecosystem with a package manager, many well-designed libraries, and helpful tutorials","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Even if Julia is not available by default on your computer cluster, it is easy to set up your own local install of Julia on a cluster.","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"However, some good reasons to use the C++ version of ITensor are:","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"using ITensor within existing C++ codes\nyou already have expertise in C++ programming\nmultithreading support in C++, such as with OpenMP, offer certain sophisticated features compared to Julia multithreading (though Julia's support for multithreading has other benefits such as composability and is rapidly improving)\nyou need other specific features of C++, such as control over memory management or instant start-up times","category":"page"},{"location":"faq/JuliaAndCpp.html#Which-is-faster:-Julia-or-C-?","page":"Programming Language (Julia, C++, ...) FAQs","title":"Which is faster: Julia or C++ ?","text":"","category":"section"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Julia and C++ offer about the same performance.","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Each language gets compiled to optimized assembly code and offer arrays and containers which can efficiently stored and iterated. Well-written Julia code can be even faster than comparable C++ codes in many cases.","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"The longer answer is of course that it depends:","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Julia is a more productive language than C++, with many highly-optimized libraries for numerical computing tasks, and excellent tools for profiling and benchmarking. These features help significantly to tune Julia codes for optimal performance.\nC++ offers much more fine-grained control over memory management, which can enhance performance in certain applications and control memory usage.\nJulia codes can slow down significantly during refactoring or when introducing new code if certain best practices are not followed. The most important of these is writing type-stable code. For more details see the Performance Tips section of the Julia documentation.\nC++ applications start instantly, while Julia codes can be slow to start. However, if this start-up time is subtracted, the rest of the time of running a Julia application is similar to C++.","category":"page"},{"location":"faq/JuliaAndCpp.html#Why-did-you-choose-Julia-over-Python-for-ITensor?","page":"Programming Language (Julia, C++, ...) FAQs","title":"Why did you choose Julia over Python for ITensor?","text":"","category":"section"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Julia offers much better performance than Python, while still having nearly all of Python's benefits. One consequence is that ITensor can be written purely in Julia, whereas to write high-performance Python libraries it is necessary to implement many parts in C or C++ (the \"two-language problem\").","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"The main reasons Julia codes can easily outperform Python codes are:","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Julia is a (just-in-time) compiled language with functions specialized for the types of the arguments passed to them\nJulia arrays and containers are specialized to the types they contain, and perform similarly to C or C++ arrays when all elements have the same type\nJulia has sophisticated support for multithreading while Python has significant problems with multithreading","category":"page"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"Of course there are some drawbacks of Julia compared to Python, including a less mature ecosystem of libraries (though it is simple to call Python libraries from Julia using PyCall), and less widespread adoption.","category":"page"},{"location":"faq/JuliaAndCpp.html#Is-Julia-ITensor-a-wrapper-around-the-C-version?","page":"Programming Language (Julia, C++, ...) FAQs","title":"Is Julia ITensor a wrapper around the C++ version?","text":"","category":"section"},{"location":"faq/JuliaAndCpp.html","page":"Programming Language (Julia, C++, ...) FAQs","title":"Programming Language (Julia, C++, ...) FAQs","text":"No. The Julia version of ITensor is a complete, ground-up port of the ITensor library to the Julia language and is written 100% in Julia.","category":"page"},{"location":"examples/MPSandMPO.html#MPS-and-MPO-Examples","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"The following examples demonstrate operations available in ITensor to work with matrix product state (MPS) (or tensor train) and matrix product operator (MPO) tensor networks.","category":"page"},{"location":"examples/MPSandMPO.html#Creating-an-MPS-from-a-Tensor","page":"MPS and MPO Examples","title":"Creating an MPS from a Tensor","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"A matrix product state (MPS) made of N tensors, each with one site or physical index, is a way of representing a single tensor with N indices. One way of obtaining the MPS form of an N-index tensor T is by repeatedly factorizing T into N separate tensors using a factorization such as the Singular Value Decomposition (SVD). This algorithm for obtaining an MPS is known in the mathematics literature as the \"tensor train SVD\" or \"TT-SVD\" algorithm.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"To turn an N-index (order-N) tensor T into an MPS, you can just construct an MPS by passing T as the first argument, along with keyword arguments that control the approximations used in factorizing T. Let's look at a few specific cases.","category":"page"},{"location":"examples/MPSandMPO.html#ITensor-to-MPS-Example","page":"MPS and MPO Examples","title":"ITensor to MPS Example","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"If you have a tensor T which is an ITensor and has indices i,j,k,l,m, you can create an MPS approximation of T where the MPS has site indices i,j,k,l,m as follows:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"cutoff = 1E-8\nmaxdim = 10\nT = randomITensor(i,j,k,l,m)\nM = MPS(T,(i,j,k,l,m);cutoff=cutoff,maxdim=maxdim)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Here we used a random ITensor for illustrative purposes, but it could be any ITensor and typically tensors with additional structure are more well approximated by MPS.","category":"page"},{"location":"examples/MPSandMPO.html#Julia-Tensor-to-MPS-Example","page":"MPS and MPO Examples","title":"Julia Tensor to MPS Example","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Another situation could be where you have a Julia array or Julia tensor of dimension d^N and want to approximate it as an MPS with N site indices, each of dimension d. For example, we could have the following random Julia array of dimension 2times 2times 2 times 2 times 2:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"d = 2\nN = 5\nA = randn(d,d,d,d,d)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Alternatively, the array could be just a one dimensional array of length d^N:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"A = randn(d^N)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"To convert this array to an MPS, we will first need a collection of Index objects to use as the site indices of the MPS. We can conveniently construct an array of four indices of dimension 2 as follows:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"sites = siteinds(d,N)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Finally, we can pass our array A and our sites to the MPS constructor along with parameters controlling the truncation level of the factorizations used:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"cutoff = 1E-8\nmaxdim = 10\nM = MPS(A,sites;cutoff=cutoff,maxdim=maxdim)","category":"page"},{"location":"examples/MPSandMPO.html#Obtaining-Elements-of-a-Tensor-Represented-by-an-MPS","page":"MPS and MPO Examples","title":"Obtaining Elements of a Tensor Represented by an MPS","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"A matrix product state (MPS) or tensor train (TT) is a format for representing a large tensor having N indices in terms of N smaller tensors. Given an MPS represeting a tensor T we can obtain a particular element T^s_1 s_2 s_3 cdots s_N of that tensor using code similar to the following code below.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"In the example code below we will obtain the element T^1211212221 of the tensor T which is (implicitly) defined by the MPS psi:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"using ITensors # hide\nlet # hide\nN = 10\ns = siteinds(2,N)\nchi = 4\npsi = randomMPS(s;linkdims=chi)\n\n# Make an array of integers of the element we\n# want to obtain\nel = [1,2,1,1,2,1,2,2,2,1]\n\nV = ITensor(1.)\nfor j=1:N\n V *= (psi[j]*state(s[j],el[j]))\nend\nv = scalar(V)\n\n# v is the element we wanted to obtain:\n@show v\nend # hide","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"The call to state(s[j],el[j]) in the code above makes a single-index ITensor with the Index s[j] and the entry at location el[j] set to 1.0, with all other entries set to 0.0. Contracting this tensor with the MPS tensor at site j can be viewed as \"clamping\" or \"fixing\" the index to a set value. The resulting tensors are contracted sequentially, overwriting the ITensor V, and the final scalar value of V is the tensor element we seek.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"See below for a visual depiction of what the above code is doing:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html#Expected-Value-of-Local-Operators","page":"MPS and MPO Examples","title":"Expected Value of Local Operators","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"When using an MPS to represent a quantum wavefunction psirangle a common operation is computing the expected value langlepsihatA_jpsirangle of a local operator hatA_j acting on site j. This can be accomplished efficiently and conveniently using the expect function as:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Avals = expect(psi,\"A\")","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"where \"A\" must be an operator associated with the physical site type, or site tags, of the sites of the MPS psi. For example, the operator name could be \"Sz\" for spin sites or \"Ntot\" for electron sites. (For more information about defining such operators yourself, see the section on Extending op Function Definitions.)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"As a concrete example, consider computing the expectation value of S^z_j on every site of an MPS representing a system of N spins of size S=12. In the following example we will use a random MPS of bond dimension chi=4 but the MPS could be obtained other ways such as through a DMRG calculation.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"using ITensors # hide\nN = 10\nchi = 4\nsites = siteinds(\"S=1/2\",N)\npsi = randomMPS(sites,chi)\nmagz = expect(psi,\"Sz\")\nfor (j,mz) in enumerate(magz)\n println(\"$j $mz\")\nend","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html#Expected-Values-of-MPO-Operators","page":"MPS and MPO Examples","title":"Expected Values of MPO Operators","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"When using an MPS to represent a quantum wavefunction psirangle another common operation is computing the expected value langlepsiWpsirangle of an operator W which is represented as a matrix product operator (MPO) tensor network. A key example could be the Hamiltonian defining a quantum system.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Given an MPO W and an MPS psi, you can compute langlepsiWpsirangle by using the function inner as follows:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"ex_W = inner(psi',W,psi)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"which will return a scalar that may be either real or complex, depending on the properties of psi and W.","category":"page"},{"location":"examples/MPSandMPO.html#Computing-Correlation-Functions","page":"MPS and MPO Examples","title":"Computing Correlation Functions","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"In addition to expected values of local operators discussed above, another type of observable that is very important in physics studies are correlation functions of the form","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"C_ij = langlepsi A_i B_j psirangle","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"These can be computed efficiently for an MPS psi in ITensor using the correlation_matrix function:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"C = correlation_matrix(psi,\"A\",\"B\")","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"where \"A\" and \"B\" must be an operator names associated with the physical site type, or site tags, of the sites of the MPS psi. For example, these strings could be \"Sz\", \"S+\", or \"S-\" for spin sites, or \"Cdagup\" and \"Cup\" for electron sites. (For more information about defining such operators yourself, see the section on Extending op Function Definitions.)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"As a concrete example, say we have an MPS psi for a system of spins and want to compute the correlator langlepsiS^z_i S^z_jpsirangle. We can compute this as:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"zzcorr = correlation_matrix(psi,\"Sz\",\"Sz\")","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"See the correlation_matrix docs for more details about additional arguments you can pass to this function.","category":"page"},{"location":"examples/MPSandMPO.html#Applying-a-Single-site-Operator-to-an-MPS","page":"MPS and MPO Examples","title":"Applying a Single-site Operator to an MPS","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"In many applications one needs to modify a matrix product state (MPS) by multiplying it with an operator that acts only on a single site. This is actually a very straightforward operation and this formula shows you how to do it in ITensor.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Say we have an operator G^s_3_s_3 which which acts non-trivially on site 3 of our MPS psi as in the following diagram:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"To carry out this operation, contract the operator G with the MPS tensor for site 3, removing the prime from the s_3 index afterward:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"newA = G * psi[3]\nnewA = noprime(newA)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Finally, put the new tensor back into MPS psi to update its third MPS tensor:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"psi[3] = newA","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Afterward, we can visualize the modified MPS as:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"As a technical note, if you are working in a context where gauge or orthogonality properties of the MPS are important, such as in time evolution using two-site gates, then you may want to call psi = orthogonalize(psi, 3) before modifying the tensor at site 3, which will ensure that the MPS remains in a well-defined orthogonal gauge centered on site 3. Modifying a tensor which is left- or right-orthogonal (i.e. not the \"center\" tensor of the gauge) will destroy the gauge condition and require extra operations to restore it. (Calling orthogonalize method will automatically fix this but will have to do extra work to do so.)","category":"page"},{"location":"examples/MPSandMPO.html#Applying-a-Two-site-Operator-to-an-MPS","page":"MPS and MPO Examples","title":"Applying a Two-site Operator to an MPS","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"A very common operation with matrix product states (MPS) is multiplication by a two-site operator or \"gate\" which modifies the MPS. This procedure can be carried out in an efficient, controlled way which is adaptive in the MPS bond dimension.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Say we have an operator G^s_3 s_4_s_3 s_4 which is our gate and which acts on physical sites 3 and 4 of our MPS psi, as in the following diagram:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"To apply this gate in a controlled manner, first 'gauge' the MPS psi such that either site 3 or 4 is the orthogonality center. Here we make site 3 the center:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"psi = orthogonalize(psi, 3)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"The other MPS tensors are now either left-orthogonal or right-orthogonal and can be left out of further steps without producing incorrect results.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Next, contract the gate tensor G with the MPS tensors for sites 3 and 4","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"wf = (psi[3] * psi[4]) * G\nwf = noprime(wf)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Finally, use the singular value decomposition (SVD) to factorize the resulting tensor, multiplying the singular values into either U or V. Assign these two tensors back into the MPS to update it.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"(Image: )","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"inds3 = uniqueinds(psi[3],psi[4])\nU,S,V = svd(wf,inds3,cutoff=1E-8)\npsi[3] = U\npsi[4] = S*V","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"The call to uniqueinds(psi[3]) analyzes the indices of psi[3] and psi[4] and finds any which are unique to just psi[3], saving this collection of indices as inds3. Passing this collection of indices to the svd function tells it to treat any indices that are unique to psi[3] as the indices which should go onto the U tensor afterward. We also set a truncation error cutoff of 1E-8 in the call to svd to truncate the smallest singular values and control the size of the resulting MPS. Other cutoff values can be used, depending on the desired accuracy, as well as limits on the maximum bond dimension (maxdim keyword argument).","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Complete code example","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"psi = orthogonalize(psi, 3)\n\nwf = (psi[3] * psi[4]) * G\nwf = noprime(wf)\n\ninds3 = uniqueinds(psi[3], psi[4])\nU, S, V = svd(wf, inds3; cutoff=1E-8)\npsi[3] = U\npsi[4] = S * V","category":"page"},{"location":"examples/MPSandMPO.html#Computing-the-Entanglement-Entropy-of-an-MPS","page":"MPS and MPO Examples","title":"Computing the Entanglement Entropy of an MPS","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"A key advantage of using the matrix product state (MPS) format to represent quantum wavefunctions is that it allows one to efficiently compute the entanglement entropy of any left-right bipartition of the system in one dimension, or for a two-dimensional system any \"cut\" along the MPS path.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Say that we have obtained an MPS psi of length N and we wish to compute the entanglement entropy of a bipartition of the system into a region \"A\" which consists of sites 1,2,...,b and a region B consisting of sites b+1,b+2,...,N.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Then the following code formula can be used to accomplish this task:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"psi = orthogonalize(psi, b)\nU,S,V = svd(psi[b], (linkinds(psi, b-1)..., siteinds(psi, b)...))\nSvN = 0.0\nfor n=1:dim(S, 1)\n p = S[n,n]^2\n SvN -= p * log(p)\nend","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"As a brief explanation of the code above, the call to psi = orthogonalize(psi, b) shifts the orthogonality center to site b of the MPS.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"The call to the svd routine says to treat the link (virtual or bond) Index connecting the b'th MPS tensor psi[b] and the b'th physical Index as \"row\" indices for the purposes of the SVD (these indices will end up on U, along with the Index connecting U to S).","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"The code in the for loop iterates over the diagonal elements of the S tensor (which are the singular values from the SVD), computes their squares to obtain the probabilities of observing the various states in the Schmidt basis (i.e. eigenvectors of the left-right bipartition reduced density matrices), and puts them into the von Neumann entanglement entropy formula S_textvN = - sum_n p_n logp_n.","category":"page"},{"location":"examples/MPSandMPO.html#Sampling-from-an-MPS","page":"MPS and MPO Examples","title":"Sampling from an MPS","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"A matrix product state (MPS) can be viewed as defining a probability distribution through the Born rule, as is the case when the MPS represents a quantum wavefunction. To sample from the distribution defined by an MPS, you can use the function sample provided in ITensor. For an MPS psi call to sample(psi) returns a random sample from the distribution defined by psi. (Note that each sample is drawn anew and not from a Markov chain seeded by a previous sample; this is possible because the algorithm for sampling MPS is a `perfect' sampling algorithm with no autocorrelation.)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"In more detail, say we have a set of N site indices s and define a random MPS with these sites:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"using ITensors # hide\nN = 10 # number of sites\nd = 3 # dimension of each site\nchi = 16 # bond dimension of the MPS\ns = siteinds(d,N)\npsi = randomMPS(s;linkdims=chi)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"We can now draw some samples from this MPS as","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"v1 = sample(psi)\nv2 = sample(psi)\nv3 = sample(psi)\nprintln(v1)\nprintln(v2)\nprintln(v3)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"The integers in each of the samples represent settings of each of the MPS indices in the \"computational basis\".","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"For reasons of efficiency, the sample function requires the MPS to be in orthogonal form, orthogonalized to the first site. If it is not already in this form, it can be brought into orthogonal form by calling psi = orthogonalize(psi, 1).","category":"page"},{"location":"examples/MPSandMPO.html#Write-and-Read-an-MPS-or-MPO-to-Disk-with-HDF5","page":"MPS and MPO Examples","title":"Write and Read an MPS or MPO to Disk with HDF5","text":"","category":"section"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"info: Info\nMake sure to install the HDF5 package to use this feature. (Run julia> ] add HDF5 in the Julia REPL console.)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Writing an MPS to an HDF5 File","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Let's say you have an MPS psi which you have made or obtained from a calculation. To write it to an HDF5 file named \"myfile.h5\" you can use the following pattern:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"using HDF5\nf = h5open(\"myfile.h5\",\"w\")\nwrite(f,\"psi\",psi)\nclose(f)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Above, the string \"psi\" can actually be any string you want such as \"MPS psi\" or \"Result MPS\" and doesn't have to have the same name as the reference psi. Closing the file f is optional and you can also write other objects to the same file before closing it.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Reading an MPS from an HDF5 File","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Say you have an HDF5 file \"myfile.h5\" which contains an MPS stored as a dataset with the name \"psi\". (Which would be the situation if you wrote it as in the example above.) To read this ITensor back from the HDF5 file, use the following pattern:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"using HDF5\nf = h5open(\"myfile.h5\",\"r\")\npsi = read(f,\"psi\",MPS)\nclose(f)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Many functions which involve MPS, such as the dmrg function or the OpSum system require that you use an array of site indices which match the MPS. So when reading in an MPS from disk, do not construct a new array of site indices. Instead, you can obtain them like this: sites = siteinds(psi).","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"So for example, to create an MPO from an OpSum which has the same site indices as your MPS psi, do the following:","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"os = OpSum()\n# Then put operators into os...\n\nsites = siteinds(psi) # Get site indices from your MPS\nH = MPO(os,sites)\n\n# Compute \nenergy_psi = inner(psi',H,psi)","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Note the MPS argument to the read function, which tells Julia which read function to call and how to interpret the data stored in the HDF5 dataset named \"psi\". In the future we might lift the requirement of providing the type and have it be detected automatically from the data stored in the file.","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"Writing and Reading MPOs","category":"page"},{"location":"examples/MPSandMPO.html","page":"MPS and MPO Examples","title":"MPS and MPO Examples","text":"To write or read MPOs to or from HDF5 files, just follow the examples above but use the type MPO when reading an MPO from the file instead of the type MPS.","category":"page"},{"location":"getting_started/RunningCodes.html#Running-ITensor-and-Julia-Codes","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"","category":"section"},{"location":"getting_started/RunningCodes.html#Basic-Example-Code-Template","page":"Running ITensor and Julia Codes","title":"Basic Example Code Template","text":"","category":"section"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"The basic outline of a code which uses the ITensor library is as follows","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"using ITensors\n\nlet\n # ... your own code goes here ...\n # For example:\n i = Index(2,\"i\")\n j = Index(3,\"j\")\n T = randomITensor(i,j)\n @show T\nend","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"The reason we recommend the let...end block is that code written in the Julia global scope can have some surprising behaviors. Putting your code into a let block avoids these issues.","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"Alternatively, you can wrap your code in a function:","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"using ITensors\n\nfunction main(; d1 = 2, d2 = 3)\n # ... your own code goes here ...\n # For example:\n i = Index(d1,\"i\")\n j = Index(d2,\"j\")\n T = randomITensor(i,j)\n @show T\nend\n\nmain(; d1 = 4, d2 = 5)","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"which can be useful in interactive mode, particularly if you might want to run your code with a variety of different arguments.","category":"page"},{"location":"getting_started/RunningCodes.html#Running-a-Script","page":"Running ITensor and Julia Codes","title":"Running a Script","text":"","category":"section"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"Now say you put the above code into a file named code.jl. Then you can run this code on the command line as follows","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"$ julia code.jl","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"This script-like mode of running Julia is convenient for running longer jobs, such as on a cluster.","category":"page"},{"location":"getting_started/RunningCodes.html#Running-Interactively","page":"Running ITensor and Julia Codes","title":"Running Interactively","text":"","category":"section"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"However, sometimes you want to do rapid development when first writing and testing a code. For this kind of work, the long startup and compilation times currently incurred by the Julia compiler can be a nuisance. Fortunately a nice solution is to alternate between modifying your code then running it by loading it into an already running Julia session.","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"To set up this kind of session, take the following steps:","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"Enter the interactive mode of Julia, by inputting the command julia on the command line. You will now be in the Julia \"REPL\" (read-eval-print loop) with the prompt julia> on the left of your screen.\nTo run a code such as the code.jl file discussed above, input the command\njulia> include(\"code.jl\")\nNote that you must be in the same folder as code.jl for this to work; otherwise input the entire path to the code.jl file. The code will run and you will see its output in the REPL.\nNow say you want to modify and re-run the code. To do this, just edit the file in an editor in another window, without closing your Julia session. Now run the command\njulia> include(\"code.jl\")\nagain and your updated code will run, but this time skipping any of the precompilation overhead incurred on previous steps.","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"The above steps to running a code interactively has a big advantage that you only have to pay the startup time of compiling ITensor and other libraries you are using once. Further changes to your code only incur very small extra compilation times, facilitating rapid development.","category":"page"},{"location":"getting_started/RunningCodes.html#Compiling-an-ITensor-System-Image","page":"Running ITensor and Julia Codes","title":"Compiling an ITensor System Image","text":"","category":"section"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"The above strategy of running code in the Julia REPL (interactive mode) works well, but still incurs a large start-up penalty for the first run of your code. Fortunately there is a nice way around this issue too: compiling ITensors.jl and making a system image built by the PackageCompiler.jl library.","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"To use this approach, we have provided a convenient one-line command:","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"julia> using ITensors; ITensors.compile()","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"Once ITensors.jl is installed, you can just run this command in an interactive Julia session. It can take a few minutes to run, but you only have to run it once for a given version of ITensors.jl. When it is done, it will create a file sys_itensors.so in the directory ~/.julia/sysimages/.","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"To use the compiled system image together with Julia, run the julia command (for interactive mode or scripts) in the following way:","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"$ julia --sysimage ~/.julia/sysimages/sys_itensors.so","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"A convenient thing to do is to make an alias in your shell for this command. To do this, edit your .bashrc or .zshrc or similar file for the shell you use by adding the following line:","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"alias julia_itensors=\"julia --sysimage ~/.julia/sysimages/sys_itensors.so -e \\\"using ITensors\\\" -i \"","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"where of course you can use the command name you like when defining the alias. Now running commands like julia_itensors code.jl or julia_itensors to start an interactive session will have the ITensor system image pre-loaded and you will notice significantly faster startup times. The arguments -e \\\"using ITensors\\\" -i make it so that running julia_itensors also loads the ITensor library as soon as Julia starts up, so that you don't have to type using ITensors every time.","category":"page"},{"location":"getting_started/RunningCodes.html#Using-a-Compiled-Sysimage-in-Jupyter-or-VS-Code","page":"Running ITensor and Julia Codes","title":"Using a Compiled Sysimage in Jupyter or VS Code","text":"","category":"section"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"If you have compiled a sysimage for ITensor as shown above, you can use it in Jupyter by running the following code:","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"using IJulia\ninstallkernel(\"julia_ITensors\",\"--sysimage=~/.julia/sysimages/sys_itensors.so\")","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"in the Julia REPL (Julia console).","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"To load the ITensor sysimage in VS Code, you can add","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"\"--sysimage ~/.julia/sysimages/sys_itensors.so\"","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"as an argument under the julia.additionalArgs setting in your Settings.json file.","category":"page"},{"location":"getting_started/RunningCodes.html","page":"Running ITensor and Julia Codes","title":"Running ITensor and Julia Codes","text":"For more information on the above, see the following Julia Discourse post.","category":"page"},{"location":"getting_started/NextSteps.html#Next-Steps","page":"Next Steps","title":"Next Steps","text":"","category":"section"},{"location":"getting_started/NextSteps.html","page":"Next Steps","title":"Next Steps","text":"Try one of the Tutorials in the next section of the ITensor documentation\nBrowse the Code Examples.\nRead the ITensor Paper for a long-form introduction to the design and main features of the ITensor library\nRead the Advanced ITensor Usage Guide\nMore Julia language tutorials and resources\nFrom zero to Julia!\nThink Julia\nOfficial Julia Language Manual\nList of Resources at julialang.org","category":"page"},{"location":"tutorials/DMRG.html#dmrg_tutorial","page":"DMRG","title":"DMRG Tutorial","text":"","category":"section"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"The density matrix renormalization group (DMRG) is an algorithm for computing eigenstates of Hamiltonians (or extremal eigenvectors of large, Hermitian matrices). It computes these eigenstates in the matrix product state (MPS) format.","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"Let's see how to set up and run a DMRG calculation using the ITensor library. We will be interested in finding the ground state of the quantum Hamiltonian H given by:","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"H = sum_j=1^N-1 mathbfS_j cdot mathbfS_j+1 = sum_j=1^N-1 S^z_j S^z_j+1 + frac12 S^+_j S^-_j+1 + frac12 S^-_j S^+_j+1","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"This Hamiltonian is known as the one-dimensional Heisenberg model and we will take the spins to be S=1 spins (spin-one spins). We will consider the case of N=100 and plan to do five sweeps of DMRG (five passes over the system).","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"ITensor DMRG Code","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"Let's look at an entire, working ITensor code that will do this calculation then discuss the main steps. If you need help running the code below, see the getting started page on Running ITensor and Julia Codes.","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"using ITensors\nlet\n N = 100\n sites = siteinds(\"S=1\",N)\n\n os = OpSum()\n for j=1:N-1\n os += \"Sz\",j,\"Sz\",j+1\n os += 1/2,\"S+\",j,\"S-\",j+1\n os += 1/2,\"S-\",j,\"S+\",j+1\n end\n H = MPO(os,sites)\n\n psi0 = randomMPS(sites,10)\n\n nsweeps = 5\n maxdim = [10,20,100,100,200]\n cutoff = [1E-10]\n\n energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff)\n\n return\nend","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"Steps of The Code","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"The first two lines","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"using ITensors # hide\nN = 100\nsites = siteinds(\"S=1\",N)","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"tells the function siteinds to make an array of ITensor Index objects which have the properties of S=1 spins. This means their dimension will be 3 and they will carry the \"S=1\" tag, which will enable the next part of the code to know how to make appropriate operators for them.","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"Try printing out some of these indices to verify their properties:","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"@show sites[1]","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"The next part of the code builds the Hamiltonian:","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"os = OpSum()\nfor j=1:N-1\n os += \"Sz\",j,\"Sz\",j+1\n os += 1/2,\"S+\",j,\"S-\",j+1\n os += 1/2,\"S-\",j,\"S+\",j+1\nend\nH = MPO(os,sites)","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"An OpSum is an object which accumulates Hamiltonian terms such as \"Sz\",1,\"Sz\",2 so that they can be summed afterward into a matrix product operator (MPO) tensor network. The line of code H = MPO(os,sites) constructs the Hamiltonian in the MPO format, with physical indices given by the array sites.","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"The line","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"psi0 = randomMPS(sites,10)","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"constructs an MPS psi0 which has the physical indices sites and a bond dimension of 10. It is made by a random quantum circuit that is reshaped into an MPS, so that it will have as generic and unbiased properties as an MPS of that size can have. This choice can help prevent the DMRG calculation from getting stuck in a local minimum.","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"The lines","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"nsweeps = 5\nmaxdim = [10,20,100,100,200]\ncutoff = [1E-10]","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"define the number of DMRG sweeps (five) we will instruct the code to do, as well as the parameters that will control the speed and accuracy of the DMRG algorithm within each sweep. The array maxdim limits the maximum MPS bond dimension allowed during each sweep and cutoff defines the truncation error goal of each sweep (if fewer values are specified than sweeps, the last value is used for all remaining sweeps).","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"Finally the call","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff)","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"runs the DMRG algorithm included in ITensor, using psi0 as an initial guess for the ground state wavefunction. The optimized MPS psi and its eigenvalue energy are returned.","category":"page"},{"location":"tutorials/DMRG.html","page":"DMRG","title":"DMRG","text":"After the dmrg function returns, you can take the returned MPS psi and do further calculations with it, such as measuring local operators or computing entanglement entropy.","category":"page"},{"location":"Multithreading.html#Multithreading","page":"Multithreading","title":"Multithreading","text":"","category":"section"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"Most modern computers, including laptops, have multiple cores (processing units) which can be used to perform multiple tasks at the same time and therefore speed up computations. Multithreading is a form of shared memory parallelism that makes use of these multiple cores that you may have available.","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"There are three primary sources of parallelization available to ITensors.jl. These are:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"BLAS/LAPACK multithreading (through whatever flavor you are using, i.e. OpenBLAS or MKL).\nThe Strided.jl package, which implements efficient multithreaded dense array permutations.\nBlock sparse multithreading (currently only for block sparse contractions) implemented in the NDTensors.jl package.","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"First, you can obtain the number of threads that are available to you with:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"julia> Sys.CPU_THREADS\n6","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"If your computations are dominated by large dense tensors, you likely want to make use of BLAS multithreading in order to multithread dense matrix multiplications and other linear algebra methods like SVD and QR decompositions. This will be on by default. The BLAS/LAPACK multithreading can be controlled in the usual way with environment variables such as by starting Julia with:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"$ MKL_NUM_THREADS=4 julia # Set the number of MKL threads to 4\n\n$ OPENBLAS_NUM_THREADS=4 julia # Set the number of OpenBLAS threads to 4\n\n$ OMP_NUM_THREADS=4 julia # Set the number of OpenMP threads to 4, which will be used by MKL or OpenBLAS if they are not specifically set","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"or at runtime from within Julia:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"julia> using LinearAlgebra\n\njulia> BLAS.vendor() # Check which BLAS you are using\n:mkl\n\njulia> using ITensors\n\njulia> BLAS.get_num_threads()\n6\n\njulia> BLAS.set_num_threads(4)\n\njulia> BLAS.get_num_threads()\n4","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"Note that in Julia v1.6, you will be able to use the command using LinearAlgebra; BLAS.get_num_threads().","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"We would highly recommend using MKL (see the installation instructions for how to do that), especially if you are using an Intel chip. How well BLAS multithreading will work depends on how much your calculations are dominated by large dense matrix operations (which is not always the case, especially if you are using QN conservation).","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"Currently, ITensors.jl makes use of the package Strided.jl for performant dense array permutations. It also provides multithreaded array permutations. If you start Julia with multiple threads, Strided multithreading is on by default:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"$ julia -t 4\n\njulia> Threads.nthreads()\n4\n\njulia> ITensors.Strided.get_num_threads()\n4","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"We find that this threading competes with BLAS threading as well as ITensors.jl's own block sparse multithreading, so if you are using Julia with multiple threads you may want to disable Strided.jl's threading with:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"julia> ITensors.Strided.disable_threads()\n1\n\njulia> ITensors.Strided.get_num_threads()\n1","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"in favor of either BLAS threading or ITensors.jl's block sparse threading.","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"Additionally, ITensors.jl, through the NDTensors.jl library, provides multithreaded block sparse operations. By default, this kind of threading is disabled. If your computations involve QN conserving tensors, you may want to consider enabling block sparse multithreading as described below.","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"ITensors.enable_threaded_blocksparse","category":"page"},{"location":"Multithreading.html#ITensors.enable_threaded_blocksparse","page":"Multithreading","title":"ITensors.enable_threaded_blocksparse","text":"ITensors.enable_threaded_blocksparse()\nITensors.disable_threaded_blocksparse()\n\nEnable or disable block sparse multithreading.\n\nReturns the current state of ITensors.using_threaded_blocksparse(), i.e. true if threaded block sparse was previously enabled, and false if threaded block sparse was previously disabled. This is helpful for turning block sparse threading on or off temporarily. For example:\n\nusing_threaded_blocksparse = ITensors.enable_threaded_blocksparse()\n# Run code that you want to be threaded\nif !using_threaded_blocksparse\n ITensors.disable_threaded_blocksparse()\nend\n\nNote that you need to start Julia with multiple threads. For example, to start Julia with 4 threads, you can use any of the following:\n\n$ julia --threads=4\n\n$ julia -t 4\n\n$ JULIA_NUM_THREADS=4 julia\n\nIn addition, we have found that it is best to disable BLAS and Strided multithreading when using block sparse multithreading. You can do that with the commands using LinearAlgebra; BLAS.set_num_threads(1) and ITensors.Strided.disable_threads().\n\nSee also: ITensors.enable_threaded_blocksparse, ITensors.disable_threaded_blocksparse, ITensors.using_threaded_blocksparse.\n\n\n\n\n\nenable_threaded_blocksparse(enable::Bool)\n\nenable_threaded_blocksparse(true) enables threaded block sparse operations (equivalent to enable_threaded_blocksparse()).\n\nenable_threaded_blocksparse(false) disables threaded block sparse operations (equivalent to enable_threaded_blocksparse()).\n\n\n\n\n\n","category":"function"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"Here is a simple example of using block sparse multithreading to speed up a sparse tensor contraction:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"using BenchmarkTools\nusing ITensors\nusing LinearAlgebra\n\nfunction main(; d = 20, order = 4)\n BLAS.set_num_threads(1)\n ITensors.Strided.set_num_threads(1)\n\n println(\"#################################################\")\n println(\"# order = \", order)\n println(\"# d = \", d)\n println(\"#################################################\")\n println()\n\n i(n) = Index(QN(0) => d, QN(1) => d; tags = \"i$n\")\n is = IndexSet(i, order ÷ 2)\n A = randomITensor(is'..., dag(is)...)\n B = randomITensor(is'..., dag(is)...)\n\n ITensors.enable_threaded_blocksparse(false)\n\n println(\"Serial contract:\")\n @disable_warn_order begin\n C_contract = @btime $A' * $B samples = 5\n end\n println()\n\n println(\"Threaded contract:\")\n @disable_warn_order begin\n ITensors.enable_threaded_blocksparse(true)\n C_threaded_contract = @btime $A' * $B samples = 5\n ITensors.enable_threaded_blocksparse(false)\n end\n println()\n @show C_contract ≈ C_threaded_contract\n return nothing\nend\n\nmain(d = 20, order = 4)","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"which outputs the following on a laptop with 6 threads, starting Julia with 5 threads:","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"julia> main(d = 20, order = 4)\n#################################################\n# order = 4\n# d = 20\n#################################################\n\nThreads.nthreads() = 5\nSys.CPU_THREADS = 6\nBLAS.get_num_threads() = 1\nITensors.Strided.get_num_threads() = 1\n\nSerial contract:\n 21.558 ms (131 allocations: 7.34 MiB)\n\nThreaded contract:\n 5.934 ms (446 allocations: 7.37 MiB)\n\nC_contract ≈ C_threaded_contract = true","category":"page"},{"location":"Multithreading.html","page":"Multithreading","title":"Multithreading","text":"In addition, we plan to add more threading to other parts of the code beyond contraction (such as SVD) and improve composibility with other forms of threading like BLAS and Strided, so stay tuned!","category":"page"},{"location":"ProjMPOSum.html#ProjMPOSum","page":"ProjMPOSum","title":"ProjMPOSum","text":"","category":"section"},{"location":"ProjMPOSum.html#Description","page":"ProjMPOSum","title":"Description","text":"","category":"section"},{"location":"ProjMPOSum.html","page":"ProjMPOSum","title":"ProjMPOSum","text":"ProjMPOSum","category":"page"},{"location":"ProjMPOSum.html#ITensors.ITensorMPS.ProjMPOSum","page":"ProjMPOSum","title":"ITensors.ITensorMPS.ProjMPOSum","text":"A ProjMPOSum computes and stores the projection of an implied sum of MPOs into a basis defined by an MPS, leaving a certain number of site indices of each MPO unprojected. Which sites are unprojected can be shifted by calling the position! method. The MPOs used as input to a ProjMPOSum are not added together beforehand; instead when the product method of a ProjMPOSum is invoked, each projected MPO in the set of MPOs is multiplied by the input tensor one-by-one in an efficient way.\n\nDrawing of the network represented by a ProjMPOSum P([H1,H2,...]), showing the case of nsite(P)==2 and position!(P,psi,4) for an MPS psi (note the sum Σⱼ on the left):\n\n o--o--o- -o--o--o--o--o--o \n\n\n\n\n\n","category":"type"},{"location":"ProjMPOSum.html#Methods","page":"ProjMPOSum","title":"Methods","text":"","category":"section"},{"location":"ProjMPOSum.html","page":"ProjMPOSum","title":"ProjMPOSum","text":"product(::ProjMPOSum,::ITensor)\nposition!(::ProjMPOSum, ::MPS, ::Int)\nnoiseterm(::ProjMPOSum,::ITensor,::String)","category":"page"},{"location":"ProjMPOSum.html#ITensors.product-Tuple{ProjMPOSum, ITensor}","page":"ProjMPOSum","title":"ITensors.product","text":"product(P::ProjMPOSum,v::ITensor)\n\n(P::ProjMPOSum)(v::ITensor)\n\nEfficiently multiply the ProjMPOSum P by an ITensor v in the sense that the ProjMPOSum is a generalized square matrix or linear operator and v is a generalized vector in the space where it acts. The returned ITensor will have the same indices as v. The operator overload P(v) is shorthand for product(P,v).\n\n\n\n\n\n","category":"method"},{"location":"ProjMPOSum.html#ITensors.ITensorMPS.position!-Tuple{ProjMPOSum, MPS, Int64}","page":"ProjMPOSum","title":"ITensors.ITensorMPS.position!","text":"position!(P::ProjMPOSum, psi::MPS, pos::Int)\n\nGiven an MPS psi, shift the projection of the MPO represented by the ProjMPOSum P such that the set of unprojected sites begins with site pos. This operation efficiently reuses previous projections of the MPOs on sites that have already been projected. The MPS psi must have compatible bond indices with the previous projected MPO tensors for this operation to succeed.\n\n\n\n\n\n","category":"method"},{"location":"ProjMPOSum.html#ITensors.ITensorMPS.noiseterm-Tuple{ProjMPOSum, ITensor, String}","page":"ProjMPOSum","title":"ITensors.ITensorMPS.noiseterm","text":"noiseterm(P::ProjMPOSum,\n phi::ITensor,\n ortho::String)\n\nReturn a \"noise term\" or density matrix perturbation ITensor as proposed in Phys. Rev. B 72, 180403 for aiding convergence of DMRG calculations. The ITensor phi is the contracted product of MPS tensors acted on by the ProjMPOSum P, and ortho is a String which can take the values \"left\" or \"right\" depending on the sweeping direction of the DMRG calculation.\n\n\n\n\n\n","category":"method"},{"location":"ProjMPOSum.html#Properties","page":"ProjMPOSum","title":"Properties","text":"","category":"section"},{"location":"ProjMPOSum.html","page":"ProjMPOSum","title":"ProjMPOSum","text":"eltype(::ProjMPOSum)\nsize(::ProjMPOSum)","category":"page"},{"location":"ProjMPOSum.html#Base.eltype-Tuple{ProjMPOSum}","page":"ProjMPOSum","title":"Base.eltype","text":"eltype(P::ProjMPOSum)\n\nDeduce the element type (such as Float64 or ComplexF64) of the tensors in the ProjMPOSum P.\n\n\n\n\n\n","category":"method"},{"location":"ProjMPOSum.html#Base.size-Tuple{ProjMPOSum}","page":"ProjMPOSum","title":"Base.size","text":"size(P::ProjMPOSum)\n\nThe size of a ProjMPOSum are its dimensions (d,d) when viewed as a matrix or linear operator acting on a space of dimension d.\n\nFor example, if a ProjMPOSum maps from a space with indices (a,s1,s2,b) to the space (a',s1',s2',b') then the size is (d,d) where d = dim(a)*dim(s1)*dim(s1)*dim(b)\n\n\n\n\n\n","category":"method"},{"location":"SiteType.html#SiteType-and-op,-state,-val-functions","page":"SiteType and op, state, val functions","title":"SiteType and op, state, val functions","text":"","category":"section"},{"location":"SiteType.html#Description","page":"SiteType and op, state, val functions","title":"Description","text":"","category":"section"},{"location":"SiteType.html","page":"SiteType and op, state, val functions","title":"SiteType and op, state, val functions","text":"SiteType","category":"page"},{"location":"SiteType.html#ITensors.SiteTypes.SiteType","page":"SiteType and op, state, val functions","title":"ITensors.SiteTypes.SiteType","text":"SiteType is a parameterized type which allows making Index tags into Julia types. Use cases include overloading functions such as op, siteinds, and state which generate custom operators, Index arrays, and IndexVals associated with Index objects having a certain tag.\n\nTo make a SiteType type, you can use the string macro notation: SiteType\"MyTag\"\n\nTo make a SiteType value or object, you can use the notation: SiteType(\"MyTag\")\n\nThere are currently a few built-in site types recognized by jl. The system is easily extensible by users. To add new operators to an existing site type, or to create new site types, you can follow the instructions here.\n\nThe current built-in site types are:\n\nSiteType\"S=1/2\" (or SiteType\"S=½\")\nSiteType\"S=1\"\nSiteType\"Qubit\"\nSiteType\"Qudit\"\nSiteType\"Boson\"\nSiteType\"Fermion\"\nSiteType\"tJ\"\nSiteType\"Electron\"\n\nExamples\n\nTags on indices get turned into SiteTypes internally, and then we search for overloads of functions like op and siteind. For example:\n\njulia> s = siteind(\"S=1/2\")\n(dim=2|id=862|\"S=1/2,Site\")\n\njulia> @show op(\"Sz\", s);\nop(s, \"Sz\") = ITensor ord=2\nDim 1: (dim=2|id=862|\"S=1/2,Site\")'\nDim 2: (dim=2|id=862|\"S=1/2,Site\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 0.5 0.0\n 0.0 -0.5\n\njulia> @show op(\"Sx\", s);\nop(s, \"Sx\") = ITensor ord=2\nDim 1: (dim=2|id=862|\"S=1/2,Site\")'\nDim 2: (dim=2|id=862|\"S=1/2,Site\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 0.0 0.5\n 0.5 0.0\n\njulia> @show op(\"Sy\", s);\nop(s, \"Sy\") = ITensor ord=2\nDim 1: (dim=2|id=862|\"S=1/2,Site\")'\nDim 2: (dim=2|id=862|\"S=1/2,Site\")\nNDTensors.Dense{Complex{Float64},Array{Complex{Float64},1}}\n 2×2\n 0.0 + 0.0im -0.0 - 0.5im\n 0.0 + 0.5im 0.0 + 0.0im\n\njulia> s = siteind(\"Electron\")\n(dim=4|id=734|\"Electron,Site\")\n\njulia> @show op(\"Nup\", s);\nop(s, \"Nup\") = ITensor ord=2\nDim 1: (dim=4|id=734|\"Electron,Site\")'\nDim 2: (dim=4|id=734|\"Electron,Site\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n 4×4\n 0.0 0.0 0.0 0.0\n 0.0 1.0 0.0 0.0\n 0.0 0.0 0.0 0.0\n 0.0 0.0 0.0 1.0\n\nMany operators are available, for example:\n\nSiteType\"S=1/2\": \"Sz\", \"Sx\", \"Sy\", \"S+\", \"S-\", ...\nSiteType\"Electron\": \"Nup\", \"Ndn\", \"Nupdn\", \"Ntot\", \"Cup\", \"Cdagup\", \"Cdn\", \"Cdagdn\", \"Sz\", \"Sx\", \"Sy\", \"S+\", \"S-\", ...\n...\n\nYou can view the source code for the internal SiteType definitions and operators that are defined here.\n\n\n\n\n\n","category":"type"},{"location":"SiteType.html#Methods","page":"SiteType and op, state, val functions","title":"Methods","text":"","category":"section"},{"location":"SiteType.html","page":"SiteType and op, state, val functions","title":"SiteType and op, state, val functions","text":"op\nstate\nval\nspace","category":"page"},{"location":"SiteType.html#ITensors.SiteTypes.op","page":"SiteType and op, state, val functions","title":"ITensors.SiteTypes.op","text":"op(opname::String, s::Index; kwargs...)\n\nReturn an ITensor corresponding to the operator named opname for the Index s. The operator is constructed by calling an overload of either the op or op! methods which take a SiteType argument that corresponds to one of the tags of the Index s and an OpName\"opname\" argument that corresponds to the input operator name.\n\nOperator names can be combined using the \"*\" symbol, for example \"S+*S-\" or \"Sz*Sz*Sz\". The result is an ITensor made by forming each operator then contracting them together in a way corresponding to the usual operator product or matrix multiplication.\n\nThe op system is used by the OpSum system to convert operator names into ITensors, and can be used directly such as for applying operators to MPS.\n\nExample\n\ns = Index(2, \"Site,S=1/2\")\nSz = op(\"Sz\", s)\n\nTo see all of the operator names defined for the site types included with ITensor, please view the source code for each site type. Note that some site types such as \"S=1/2\" and \"Qubit\" are aliases for each other and share operator definitions.\n\n\n\n\n\nop(X::AbstractArray, s::Index...)\nop(M::Matrix, s::Index...)\n\nGiven a matrix M and a set of indices s,t,... return an operator ITensor with matrix elements given by M and indices s, s', t, t'\n\nExample\n\njulia> s = siteind(\"S=1/2\")\n(dim=2|id=575|\"S=1/2,Site\")\n\njulia> Sz = op([1/2 0; 0 -1/2],s)\nITensor ord=2 (dim=2|id=575|\"S=1/2,Site\")' (dim=2|id=575|\"S=1/2,Site\")\nNDTensors.Dense{Float64, Vector{Float64}}\n\njulia> @show Sz\nSz = ITensor ord=2\nDim 1: (dim=2|id=575|\"S=1/2,Site\")'\nDim 2: (dim=2|id=575|\"S=1/2,Site\")\nNDTensors.Dense{Float64, Vector{Float64}}\n 2×2\n 0.5 0.0\n 0.0 -0.5\nITensor ord=2 (dim=2|id=575|\"S=1/2,Site\")' (dim=2|id=575|\"S=1/2,Site\")\nNDTensors.Dense{Float64, Vector{Float64}}\n\n\n\n\n\nop(opname::String,sites::Vector{<:Index},n::Int; kwargs...)\n\nReturn an ITensor corresponding to the operator named opname for the n'th Index in the array sites.\n\nExample\n\ns = siteinds(\"S=1/2\", 4)\nSz2 = op(\"Sz\", s, 2)\n\n\n\n\n\n","category":"function"},{"location":"SiteType.html#ITensors.SiteTypes.state","page":"SiteType and op, state, val functions","title":"ITensors.SiteTypes.state","text":"state(s::Index, name::String; kwargs...)\n\nReturn an ITensor corresponding to the state named name for the Index s. The returned ITensor will have s as its only index.\n\nThe terminology here is based on the idea of a single-site state or wavefunction in physics.\n\nThe state function is implemented for various Index tags by overloading either the state or state! methods which take a SiteType argument corresponding to one of the tags of the Index s and an StateName\"name\" argument that corresponds to the input state name.\n\nThe state system is used by the MPS type to construct product-state MPS and for other purposes.\n\nExample\n\ns = Index(2, \"Site,S=1/2\")\nsup = state(s,\"Up\")\nsdn = state(s,\"Dn\")\nsxp = state(s,\"X+\")\nsxm = state(s,\"X-\")\n\n\n\n\n\n","category":"function"},{"location":"SiteType.html#ITensors.val","page":"SiteType and op, state, val functions","title":"ITensors.val","text":"val(q::QN,name)\n\nGet the value within the QN q corresponding to the string name\n\n\n\n\n\nval(s::Index, name::String)\n\nReturn an integer corresponding to the name of a certain value the Index s can take. In other words, the val function maps strings to specific integer values within the range 1:dim(s).\n\nThe val function is implemented for various Index tags by overloading methods named val which take a SiteType argument corresponding to one of the tags of the Index s and an ValName\"name\" argument that corresponds to the input name.\n\nExample\n\ns = Index(2, \"Site,S=1/2\")\nval(s,\"Up\") == 1\nval(s,\"Dn\") == 2\n\ns = Index(2, \"Site,Fermion\")\nval(s,\"Emp\") == 1\nval(s,\"Occ\") == 2\n\n\n\n\n\n","category":"function"},{"location":"SiteType.html#ITensors.space","page":"SiteType and op, state, val functions","title":"ITensors.space","text":"space(::SiteType\"Qubit\";\n conserve_qns = false,\n conserve_parity = conserve_qns,\n conserve_number = false,\n qnname_parity = \"Parity\",\n qnname_number = \"Number\")\n\nCreate the Hilbert space for a site of type \"Qubit\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\nspace(::SiteType\"S=1/2\";\n conserve_qns = false,\n conserve_sz = conserve_qns,\n conserve_szparity = false,\n qnname_sz = \"Sz\",\n qnname_szparity = \"SzParity\")\n\nCreate the Hilbert space for a site of type \"S=1/2\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\nspace(::SiteType\"S=1\";\n conserve_qns = false,\n conserve_sz = conserve_qns,\n qnname_sz = \"Sz\")\n\nCreate the Hilbert space for a site of type \"S=1\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\nspace(::SiteType\"Fermion\";\n conserve_qns=false,\n conserve_nf=conserve_qns,\n conserve_nfparity=conserve_qns,\n qnname_nf = \"Nf\",\n qnname_nfparity = \"NfParity\",\n qnname_sz = \"Sz\",\n conserve_sz = false)\n\nCreate the Hilbert space for a site of type \"Fermion\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\nspace(::SiteType\"Electron\";\n conserve_qns = false,\n conserve_sz = conserve_qns,\n conserve_nf = conserve_qns,\n conserve_nfparity = conserve_qns,\n qnname_sz = \"Sz\",\n qnname_nf = \"Nf\",\n qnname_nfparity = \"NfParity\")\n\nCreate the Hilbert space for a site of type \"Electron\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\nspace(::SiteType\"tJ\";\n conserve_qns = false,\n conserve_sz = conserve_qns,\n conserve_nf = conserve_qns,\n conserve_nfparity = conserve_qns,\n qnname_sz = \"Sz\",\n qnname_nf = \"Nf\",\n qnname_nfparity = \"NfParity\")\n\nCreate the Hilbert space for a site of type \"tJ\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\nspace(::SiteType\"Qudit\";\n dim = 2,\n conserve_qns = false,\n conserve_number = false,\n qnname_number = \"Number\")\n\nCreate the Hilbert space for a site of type \"Qudit\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\nspace(::SiteType\"Boson\";\n dim = 2,\n conserve_qns = false,\n conserve_number = false,\n qnname_number = \"Number\")\n\nCreate the Hilbert space for a site of type \"Boson\".\n\nOptionally specify the conserved symmetries and their quantum number labels.\n\n\n\n\n\n","category":"function"},{"location":"Einsum.html#ITensor-Index-identity:-dimension-labels-and-Einstein-notation","page":"ITensor indices and Einstein notation","title":"ITensor Index identity: dimension labels and Einstein notation","text":"","category":"section"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"Many tensor contraction libraries use Einstein notation, such as NumPy's einsum function, ncon, and various Julia packages such as TensorOperations.jl, Tullio.jl, OMEinsum.jl, and Einsum.jl, among others.","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"ITensor also uses Einstein notation, however the labels are stored inside the tensor and carried around with them during various operations. In addition, the labels that determine if tensor indices match with each other, and therefore automatically contract when doing * or match when adding or subtracting, are more sophisticated than simple characters or strings. ITensor indices are given a unique random ID number when they are constructed, and additionally users can add additional information like prime levels and tags which uniquely determine an Index. This is in contrast to simpler implementations of the same idea, such as the NamedDims.jl package, which only allow symbols as the metadata for uniquely identifying a tensor/array dimension.","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"using ITensors\nusing Random\nRandom.seed!(1)","category":"page"},{"location":"Einsum.html#Index-identity","page":"ITensor indices and Einstein notation","title":"Index identity","text":"","category":"section"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"Here is an illustration of how the different types of Index metadata (random ID, prime level, and tags) work for Index identity:","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"i = Index(2)\nj = Index(2)\ni == j\nid(i)\nid(j)\nip = i'\nip == i\nplev(i) == 0\nplev(ip) == 1\nnoprime(ip) == i\nix = addtags(i, \"x\")\nix == i\nremovetags(ix, \"x\") == i\nixyz = addtags(ix, \"y,z\")\nixyz == addtags(i, \"z,y,x\")","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"The different metadata that are stored inside of ITensor indices that determine their identity are useful in different contexts. The random ID is particularly useful in the case when a new Index needs to be generated internally by ITensor, such as when performing a matrix factorization. In the case of a matrix factorization, we want to make sure that the new Index will not accidentally clash with an existing one, for example:","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"i = Index(2, \"i\")\nj = Index(2, \"j\")\nA = randomITensor(i, j)\nU, S, V = svd(A, i; lefttags=\"i\", righttags=\"j\");\ninds(U)\ninds(S)\ninds(V)\nnorm(U * S * V - A)","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"You can see that it would have been a problem here if there wasn't a new ID assigned to the Index, since it would have clashed with the original index. In this case, it could be avoided by giving the new indices different tags (with the keyword arguments lefttags and righttags), but in more complicated examples where it is not practical to do that (such as a case where many new indices are being introduced, for example for a tensor train (TT)/matrix product state (MPS)), it is convenient to not force users to come up with unique prime levels or tags themselves. It can also help to avoid accidental contractions in more complicated tensor network algorithms where there are many indices that can potentially have the same prime levels or tags.","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"In contrast, using multiple indices with the same Index ID but different prime levels and tags can be useful in situations where there is a more fundamental relationship between the spaces. For example, in the case of an ITensor corresponding to a Hermitian operator, it is helpful to make the bra space and ket spaces the same up to a prime level:","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"i = Index(2, \"i\")\nj = Index(3, \"j\")\nA = randomITensor(i', j', dag(i), dag(j))\nH = 0.5 * (A + swapprime(dag(A), 0 => 1))\nv = randomITensor(i, j)\nHv = noprime(H * v)\nvH = dag(v)' * H\nnorm(Hv - dag(vH))","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"Note that we have added dag in a few places, which is superfluous in this case since the tensors are real and dense but become important when the tensors are complex and/or have symmetries. You can see that in this case, it is very useful to relate the bra and ket spaces by prime levels, since it makes it much easier to perform operations that map from one space to another. We could have created A from 4 entirely different indices with different ID numbers, but it would make the operations a bit more cumbersome, as shown below:","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"i = Index(2, \"i\")\nj = Index(3, \"j\")\nip = Index(2, \"i\")\njp = Index(3, \"jp\")\nA = randomITensor(ip, jp, dag(i), dag(j))\nH = 0.5 * (A + swapinds(dag(A), (i, j), (ip, jp)))\nv = randomITensor(i, j)\nHv = replaceinds(H * v, (ip, jp) => (i, j))\nvH = replaceinds(dag(v), (i, j) => (ip, jp)) * H\nnorm(Hv - dag(vH))","category":"page"},{"location":"Einsum.html#Relationship-to-other-Einstein-notation-based-libraries","page":"ITensor indices and Einstein notation","title":"Relationship to other Einstein notation-based libraries","text":"","category":"section"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"Here we show examples of different ways to perform the contraction \"ab,bc,cd->ad\" in ITensor.","category":"page"},{"location":"Einsum.html","page":"ITensor indices and Einstein notation","title":"ITensor indices and Einstein notation","text":"da, dc = 2, 3;\ndb, dd = da, dc;\ntags = (\"a\", \"b\", \"c\", \"d\");\ndims = (da, db, dc, dd);\na, b, c, d = Index.(dims, tags);\nAab = randomITensor(a, b)\nBbc = randomITensor(b, c)\nCcd = randomITensor(c, d)\n\n# \"ab,bc,cd->ad\"\nout1 = Aab * Bbc * Ccd\n@show hassameinds(out1, (a, d))\n\n#\n# Using replaceinds (most general way)\n#\n\n# \"ba,bc,dc->ad\"\nAba = replaceinds(Aab, (a, b) => (b, a))\nCdc = replaceinds(Ccd, (c, d) => (d, c))\nout2 = Aba * Bbc * Cdc\n@show hassameinds(out2, (a, d))\n\n#\n# Using setinds\n#\n\n# This is a bit lower level\n# since it doesn't check if the indices\n# are compatible in dimension,\n# so is not recommended in general.\nusing ITensors: setinds\n\nAba = setinds(Aab, (b, a))\nCdc = setinds(Ccd, (d, c))\nout2 = Aba * Bbc * Cdc\n@show hassameinds(out2, (a, d))\n\n#\n# Using prime levels (assuming\n# the indices were made with these\n# prime levels in the first place)\n#\n\na = Index(da, \"a\")\nc = Index(dc, \"c\")\nb, d = a', c'\nAab = randomITensor(a, b)\nBbc = randomITensor(b, c)\nCcd = randomITensor(c, d)\nout1 = Aab * Bbc * Ccd\n@show hassameinds(out1, (a, d))\n\nAba = swapprime(Aab, 0 => 1)\nCdc = swapprime(Ccd, 0 => 1)\nout2 = Aba * Bbc * Cdc\n@show hassameinds(out2, (a, d))\n\n#\n# Using tags (assuming\n# the indices were made with these\n# tags in the first place)\n#\n\na = Index(da, \"a\")\nc = Index(dc, \"c\")\nb, d = settags(a, \"b\"), settags(c, \"d\")\nAab = randomITensor(a, b)\nBbc = randomITensor(b, c)\nCcd = randomITensor(c, d)\nout1 = Aab * Bbc * Ccd\n@show hassameinds(out1, (a, d))\n\nAba = swaptags(Aab, \"a\", \"b\")\nCdc = swaptags(Ccd, \"c\", \"d\")\nout2 = Aba * Bbc * Cdc\n@show hassameinds(out2, (a, d))\n\n#\n# Using Julia Arrays\n#\n\nA = randn(da, db)\nB = randn(db, dc)\nC = randn(dc, dd)\n\ntags = (\"a\", \"b\", \"c\", \"d\")\ndims = (da, db, dc, dd)\na, b, c, d = Index.(dims, tags)\n\nAab = itensor(A, a, b)\nBbc = itensor(B, b, c)\nCcd = itensor(C, c, d)\nout1 = Aab * Bbc * Ccd\n@show hassameinds(out1, (a, d))\n\nAba = itensor(A, b, a)\nCdc = itensor(C, d, c)\nout2 = Aba * Bbc * Cdc\n@show hassameinds(out2, (a, d))\n\n#\n# Note that we may start allowing\n# this notation in future:\n# (https://github.com/ITensor/ITensors.jl/issues/673)\n#\n#out1 = A[a, b] * B[b, c] * C[c, d]\n#@show hassameinds(out1, (a, d))\n#\n#out2 = A[b, a] * B[b, c] * C[d, c]\n#@show hassameinds(out2, (a, d))","category":"page"},{"location":"ITensorType.html#ITensor","page":"ITensor","title":"ITensor","text":"","category":"section"},{"location":"ITensorType.html#Description","page":"ITensor","title":"Description","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"ITensor","category":"page"},{"location":"ITensorType.html#ITensors.ITensor","page":"ITensor","title":"ITensors.ITensor","text":"ITensor\n\nAn ITensor is a tensor whose interface is independent of its memory layout. Therefore it is not necessary to know the ordering of an ITensor's indices, only which indices an ITensor has. Operations like contraction and addition of ITensors automatically handle any memory permutations.\n\nExamples\n\njulia> i = Index(2, \"i\")\n(dim=2|id=287|\"i\")\n\n#\n# Make an ITensor with random elements:\n#\njulia> A = randomITensor(i', i)\nITensor ord=2 (dim=2|id=287|\"i\")' (dim=2|id=287|\"i\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=287|\"i\")'\nDim 2: (dim=2|id=287|\"i\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 0.28358594718392427 1.4342219756446355\n 1.6620103556283987 -0.40952231269251566\n\njulia> @show inds(A);\ninds(A) = ((dim=2|id=287|\"i\")', (dim=2|id=287|\"i\"))\n\n#\n# Set the i==1, i'==2 element to 1.0:\n#\njulia> A[i => 1, i' => 2] = 1;\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=287|\"i\")'\nDim 2: (dim=2|id=287|\"i\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 0.28358594718392427 1.4342219756446355\n 1.0 -0.40952231269251566\n\njulia> @show storage(A);\nstorage(A) = [0.28358594718392427, 1.0, 1.4342219756446355, -0.40952231269251566]\n\njulia> B = randomITensor(i, i');\n\njulia> @show B;\nB = ITensor ord=2\nDim 1: (dim=2|id=287|\"i\")\nDim 2: (dim=2|id=287|\"i\")'\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n -0.6510816500352691 0.2579101497658179\n 0.256266641521826 -0.9464735926768166\n\n#\n# Can add or subtract ITensors as long as they\n# have the same indices, in any order:\n#\njulia> @show A + B;\nA + B = ITensor ord=2\nDim 1: (dim=2|id=287|\"i\")'\nDim 2: (dim=2|id=287|\"i\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n -0.3674957028513448 1.6904886171664615\n 1.2579101497658178 -1.3559959053693322\n\n\n\n\n\n","category":"type"},{"location":"ITensorType.html#Dense-Constructors","page":"ITensor","title":"Dense Constructors","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"ITensor(::Type{<:Number}, ::ITensors.Indices)\nITensor(::Type{<:Number}, ::UndefInitializer, ::ITensors.Indices)\nITensor(::Type{<:Number}, ::Number, ::ITensors.Indices)\nITensor(::ITensors.AliasStyle, ::Type{<:Number}, ::Array{<:Number}, ::ITensors.Indices{Index{Int}}; kwargs...)\nrandomITensor(::Type{<:Number}, ::ITensors.Indices)\nonehot","category":"page"},{"location":"ITensorType.html#ITensors.ITensor-Tuple{Type{<:Number}, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.ITensor","text":"ITensor([::Type{ElT} = Float64, ]inds)\nITensor([::Type{ElT} = Float64, ]inds::Index...)\n\nConstruct an ITensor filled with zeros having indices inds and element type ElT. If the element type is not specified, it defaults to Float64.\n\nThe storage will have NDTensors.Dense type.\n\nExamples\n\ni = Index(2,\"index_i\")\nj = Index(4,\"index_j\")\nk = Index(3,\"index_k\")\n\nA = ITensor(i,j)\nB = ITensor(ComplexF64,k,j)\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.ITensor-Tuple{Type{<:Number}, UndefInitializer, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.ITensor","text":"ITensor([::Type{ElT} = Float64, ]::UndefInitializer, inds)\nITensor([::Type{ElT} = Float64, ]::UndefInitializer, inds::Index...)\n\nConstruct an ITensor filled with undefined elements having indices inds and element type ElT. If the element type is not specified, it defaults to Float64. One purpose for using this constructor is that initializing the elements in an undefined way is faster than initializing them to a set value such as zero.\n\nThe storage will have NDTensors.Dense type.\n\nExamples\n\ni = Index(2,\"index_i\")\nj = Index(4,\"index_j\")\nk = Index(3,\"index_k\")\n\nA = ITensor(undef,i,j)\nB = ITensor(ComplexF64,undef,k,j)\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.ITensor-Tuple{Type{<:Number}, Number, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.ITensor","text":"ITensor([ElT::Type, ]x::Number, inds)\nITensor([ElT::Type, ]x::Number, inds::Index...)\n\nConstruct an ITensor with all elements set to x and indices inds.\n\nIf x isa Int or x isa Complex{Int} then the elements will be set to float(x) unless specified otherwise by the first input.\n\nThe storage will have NDTensors.Dense type.\n\nExamples\n\n```julia i = Index(2,\"indexi\"); j = Index(4,\"indexj\"); k = Index(3,\"index_k\");\n\nA = ITensor(1.0, i, j) A = ITensor(1, i, j) # same as above B = ITensor(2.0+3.0im, j, k) ```\n\n!!! warning In future versions this may not automatically convert integer inputs with float, and in that case the particular element type should not be relied on.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.ITensor-Tuple{NDTensors.AliasStyle, Type{<:Number}, Array{<:Number}, Union{Tuple{Vararg{Index{Int64}}}, Vector{Index{Int64}}}}","page":"ITensor","title":"ITensors.ITensor","text":"ITensor([ElT::Type, ]A::Array, inds)\nITensor([ElT::Type, ]A::Array, inds::Index...)\n\nitensor([ElT::Type, ]A::Array, inds)\nitensor([ElT::Type, ]A::Array, inds::Index...)\n\nConstruct an ITensor from an Array A and indices inds. The ITensor will be a view of the Array data if possible (if no conversion to a different element type is necessary).\n\nIf specified, the ITensor will have element type ElT.\n\nIf the element type of A is Int or Complex{Int} and the desired element type isn't specified, it will be converted to Float64 or Complex{Float64} automatically. To keep the element type as an integer, specify it explicitly, for example with:\n\ni = Index(2, \"i\")\nA = [0 1; 1 0]\nT = ITensor(eltype(A), A, i', dag(i))\n\nExamples\n\ni = Index(2,\"index_i\")\nj = Index(2,\"index_j\")\n\nM = [1. 2;\n 3 4]\nT = ITensor(M, i, j)\nT[i => 1, j => 1] = 3.3\nM[1, 1] == 3.3\nT[i => 1, j => 1] == 3.3\n\nwarning: Warning\nIn future versions this may not automatically convert Int/Complex{Int} inputs to floating point versions with float (once tensor operations using Int/Complex{Int} are natively as fast as floating point operations), and in that case the particular element type should not be relied on. To avoid extra conversions (and therefore allocations) it is best practice to directly construct with itensor([0. 1; 1 0], i', dag(i)) if you want a floating point element type. The conversion is done as a performance optimization since often tensors are passed to BLAS/LAPACK and need to be converted to floating point types compatible with those libraries, but future projects in Julia may allow for efficient operations with more general element types (for example see https://github.com/JuliaLinearAlgebra/Octavian.jl).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.randomITensor-Tuple{Type{<:Number}, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.randomITensor","text":"randomITensor([::Type{ElT <: Number} = Float64, ]inds)\nrandomITensor([::Type{ElT <: Number} = Float64, ]inds::Index...)\n\nConstruct an ITensor with type ElT and indices inds, whose elements are normally distributed random numbers. If the element type is not specified, it defaults to Float64.\n\nExamples\n\ni = Index(2,\"index_i\")\nj = Index(4,\"index_j\")\nk = Index(3,\"index_k\")\n\nA = randomITensor(i,j)\nB = randomITensor(ComplexF64,undef,k,j)\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.onehot","page":"ITensor","title":"ITensors.onehot","text":"onehot(ivs...)\nsetelt(ivs...)\nonehot(::Type, ivs...)\nsetelt(::Type, ivs...)\n\nCreate an ITensor with all zeros except the specified value, which is set to 1.\n\nExamples\n\ni = Index(2,\"i\")\nA = onehot(i=>2)\n# A[i=>2] == 1, all other elements zero\n\n# Specify the element type\nA = onehot(Float32, i=>2)\n\nj = Index(3,\"j\")\nB = onehot(i=>1,j=>3)\n# B[i=>1,j=>3] == 1, all other element zero\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#Dense-View-Constructors","page":"ITensor","title":"Dense View Constructors","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"itensor(::Array{<:Number}, ::ITensors.Indices)","category":"page"},{"location":"ITensorType.html#ITensors.itensor-Tuple{Array{<:Number}, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.itensor","text":"itensor(args...; kwargs...)\n\nLike the ITensor constructor, but with attempt to make a view of the input data when possible.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#QN-BlockSparse-Constructors","page":"ITensor","title":"QN BlockSparse Constructors","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"ITensor(::Type{<:Number}, ::QN, ::ITensors.QNIndices)\nITensor(::ITensors.AliasStyle, ::Type{<:Number}, ::Array{<:Number}, ::ITensors.QNIndices; tol=0)\nITensor(::Type{<:Number}, ::UndefInitializer, ::QN, ::ITensors.Indices)","category":"page"},{"location":"ITensorType.html#ITensors.ITensor-Tuple{Type{<:Number}, QN, Union{Tuple{Vararg{Index{Vector{Pair{QN, Int64}}}}}, Vector{Index{Vector{Pair{QN, Int64}}}}}}","page":"ITensor","title":"ITensors.ITensor","text":"ITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]inds)\nITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]inds::Index...)\n\nConstruct an ITensor with BlockSparse storage filled with zero(ElT) where the nonzero blocks are determined by flux.\n\nIf ElT is not specified it defaults to Float64.\n\nIf flux is not specified, the ITensor will be empty (it will contain no blocks, and have an undefined flux). The flux will be set by the first element that is set.\n\nExamples\n\njulia> i\n(dim=3|id=212|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\n\njulia> @show ITensor(QN(0), i', dag(i));\nITensor(QN(0), i', dag(i)) = ITensor ord=2\nDim 1: (dim=3|id=212|\"i\")' \n 1: QN(0) => 1\n 2: QN(1) => 2\nDim 2: (dim=3|id=212|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\nNDTensors.BlockSparse{Float64, Vector{Float64}, 2}\n 3×3\nBlock(1, 1)\n [1:1, 1:1]\n 0.0\n\nBlock(2, 2)\n [2:3, 2:3]\n 0.0 0.0\n 0.0 0.0\n\njulia> @show ITensor(QN(1), i', dag(i));\nITensor(QN(1), i', dag(i)) = ITensor ord=2\nDim 1: (dim=3|id=212|\"i\")' \n 1: QN(0) => 1\n 2: QN(1) => 2\nDim 2: (dim=3|id=212|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\nNDTensors.BlockSparse{Float64, Vector{Float64}, 2}\n 3×3\nBlock(2, 1)\n [2:3, 1:1]\n 0.0\n 0.0\n\njulia> @show ITensor(ComplexF64, QN(1), i', dag(i));\nITensor(ComplexF64, QN(1), i', dag(i)) = ITensor ord=2\nDim 1: (dim=3|id=212|\"i\")' \n 1: QN(0) => 1\n 2: QN(1) => 2\nDim 2: (dim=3|id=212|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\nNDTensors.BlockSparse{ComplexF64, Vector{ComplexF64}, 2}\n 3×3\nBlock(2, 1)\n [2:3, 1:1]\n 0.0 + 0.0im\n 0.0 + 0.0im\n\njulia> @show ITensor(undef, QN(1), i', dag(i));\nITensor(undef, QN(1), i', dag(i)) = ITensor ord=2\nDim 1: (dim=3|id=212|\"i\")' \n 1: QN(0) => 1\n 2: QN(1) => 2\nDim 2: (dim=3|id=212|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\nNDTensors.BlockSparse{Float64, Vector{Float64}, 2}\n 3×3\nBlock(2, 1)\n [2:3, 1:1]\n 0.0\n 1.63e-322\n\nConstruction with undefined flux:\n\njulia> A = ITensor(i', dag(i));\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=3|id=212|\"i\")' \n 1: QN(0) => 1\n 2: QN(1) => 2\nDim 2: (dim=3|id=212|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\nNDTensors.EmptyStorage{NDTensors.EmptyNumber, NDTensors.BlockSparse{NDTensors.EmptyNumber, Vector{NDTensors.EmptyNumber}, 2}}\n 3×3\n\n\n\njulia> isnothing(flux(A))\ntrue\n\njulia> A[i' => 1, i => 2] = 2\n2\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=3|id=212|\"i\")' \n 1: QN(0) => 1\n 2: QN(1) => 2\nDim 2: (dim=3|id=212|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\nNDTensors.BlockSparse{Int64, Vector{Int64}, 2}\n 3×3\nBlock(1, 2)\n [1:1, 2:3]\n 2 0\n\njulia> flux(A)\nQN(-1)\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.ITensor-Tuple{NDTensors.AliasStyle, Type{<:Number}, Array{<:Number}, Union{Tuple{Vararg{Index{Vector{Pair{QN, Int64}}}}}, Vector{Index{Vector{Pair{QN, Int64}}}}}}","page":"ITensor","title":"ITensors.ITensor","text":"ITensor([ElT::Type, ]A::Array, inds)\nITensor([ElT::Type, ]A::Array, inds::Index...)\n\nitensor([ElT::Type, ]A::Array, inds)\nitensor([ElT::Type, ]A::Array, inds::Index...)\n\nConstruct an ITensor from an Array A and indices inds. The ITensor will be a view of the Array data if possible (if no conversion to a different element type is necessary).\n\nIf specified, the ITensor will have element type ElT.\n\nIf the element type of A is Int or Complex{Int} and the desired element type isn't specified, it will be converted to Float64 or Complex{Float64} automatically. To keep the element type as an integer, specify it explicitly, for example with:\n\ni = Index(2, \"i\")\nA = [0 1; 1 0]\nT = ITensor(eltype(A), A, i', dag(i))\n\nExamples\n\ni = Index(2,\"index_i\")\nj = Index(2,\"index_j\")\n\nM = [1. 2;\n 3 4]\nT = ITensor(M, i, j)\nT[i => 1, j => 1] = 3.3\nM[1, 1] == 3.3\nT[i => 1, j => 1] == 3.3\n\nwarning: Warning\nIn future versions this may not automatically convert Int/Complex{Int} inputs to floating point versions with float (once tensor operations using Int/Complex{Int} are natively as fast as floating point operations), and in that case the particular element type should not be relied on. To avoid extra conversions (and therefore allocations) it is best practice to directly construct with itensor([0. 1; 1 0], i', dag(i)) if you want a floating point element type. The conversion is done as a performance optimization since often tensors are passed to BLAS/LAPACK and need to be converted to floating point types compatible with those libraries, but future projects in Julia may allow for efficient operations with more general element types (for example see https://github.com/JuliaLinearAlgebra/Octavian.jl).\n\n\n\n\n\nITensor([ElT::Type, ]::AbstractArray, inds; tol=0.0, checkflux=true)\n\nCreate a block sparse ITensor from the input Array, and collection of QN indices. Zeros are dropped and nonzero blocks are determined from the zero values of the array.\n\nOptionally, you can set a tolerance such that elements less than or equal to the tolerance are dropped.\n\nBy default, this will check that the flux of the nonzero blocks are consistent with each other. You can disable this check by setting checkflux=false.\n\nExamples\n\njulia> i = Index([QN(0)=>1, QN(1)=>2], \"i\");\n\njulia> A = [1e-9 0.0 0.0;\n 0.0 2.0 3.0;\n 0.0 1e-10 4.0];\n\njulia> @show ITensor(A, i', dag(i); tol = 1e-8);\nITensor(A, i', dag(i); tol = 1.0e-8) = ITensor ord=2\nDim 1: (dim=3|id=468|\"i\")' \n 1: QN(0) => 1\n 2: QN(1) => 2\nDim 2: (dim=3|id=468|\"i\") \n 1: QN(0) => 1\n 2: QN(1) => 2\nNDTensors.BlockSparse{Float64,Array{Float64,1},2}\n 3×3\nBlock: (2, 2)\n [2:3, 2:3]\n 2.0 3.0\n 0.0 4.0\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.ITensor-Tuple{Type{<:Number}, UndefInitializer, QN, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.ITensor","text":"ITensor([::Type{ElT} = Float64,] ::UndefInitializer, flux::QN, inds)\nITensor([::Type{ElT} = Float64,] ::UndefInitializer, flux::QN, inds::Index...)\n\nConstruct an ITensor with indices inds and BlockSparse storage with undefined elements of type ElT, where the nonzero (allocated) blocks are determined by the provided QN flux. One purpose for using this constructor is that initializing the elements in an undefined way is faster than initializing them to a set value such as zero.\n\nThe storage will have NDTensors.BlockSparse type.\n\nExamples\n\ni = Index([QN(0)=>1, QN(1)=>2], \"i\")\nA = ITensor(undef,QN(0),i',dag(i))\nB = ITensor(Float64,undef,QN(0),i',dag(i))\nC = ITensor(ComplexF64,undef,QN(0),i',dag(i))\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Diagonal-constructors","page":"ITensor","title":"Diagonal constructors","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"diagITensor(::Type{<:Number}, ::ITensors.Indices)\ndiagITensor(::ITensors.AliasStyle, ::Type{<:Number}, ::Vector{<:Number}, ::ITensors.Indices)\ndiagITensor(::ITensors.AliasStyle, ::Type{<:Number}, ::Number, ::ITensors.Indices)\ndelta(::Type{<:Number}, ::ITensors.Indices)","category":"page"},{"location":"ITensorType.html#ITensors.diagITensor-Tuple{Type{<:Number}, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.diagITensor","text":"diagITensor([::Type{ElT} = Float64, ]inds)\ndiagITensor([::Type{ElT} = Float64, ]inds::Index...)\n\nMake a sparse ITensor of element type ElT with only elements along the diagonal stored. Defaults to having zero(T) along the diagonal.\n\nThe storage will have NDTensors.Diag type.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.diagITensor-Tuple{NDTensors.AliasStyle, Type{<:Number}, Vector{<:Number}, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.diagITensor","text":"diagITensor([ElT::Type, ]v::Vector, inds...)\ndiagitensor([ElT::Type, ]v::Vector, inds...)\n\nMake a sparse ITensor with non-zero elements only along the diagonal. In general, the diagonal elements will be those stored in v and the ITensor will have element type eltype(v), unless specified explicitly by ElT. The storage will have NDTensors.Diag type.\n\nIn the case when eltype(v) isa Union{Int, Complex{Int}}, by default it will be converted to float(v). Note that this behavior is subject to change in the future.\n\nThe version diagITensor will never output an ITensor whose storage data is an alias of the input vector data.\n\nThe version diagitensor might output an ITensor whose storage data is an alias of the input vector data in order to minimize operations.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.diagITensor-Tuple{NDTensors.AliasStyle, Type{<:Number}, Number, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.diagITensor","text":"diagITensor([ElT::Type, ]x::Number, inds...)\ndiagitensor([ElT::Type, ]x::Number, inds...)\n\nMake a sparse ITensor with non-zero elements only along the diagonal. In general, the diagonal elements will be set to the value x and the ITensor will have element type eltype(x), unless specified explicitly by ElT. The storage will have NDTensors.Diag type.\n\nIn the case when x isa Union{Int, Complex{Int}}, by default it will be converted to float(x). Note that this behavior is subject to change in the future.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.delta-Tuple{Type{<:Number}, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.delta","text":"delta([::Type{ElT} = Float64, ]inds)\ndelta([::Type{ElT} = Float64, ]inds::Index...)\n\nMake a uniform diagonal ITensor with all diagonal elements one(ElT). Only a single diagonal element is stored.\n\nThis function has an alias δ.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#QN-Diagonal-constructors","page":"ITensor","title":"QN Diagonal constructors","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"diagITensor(::Type{<:Number}, ::QN, ::ITensors.Indices)\ndelta(::Type{<:Number}, ::QN, ::ITensors.Indices)","category":"page"},{"location":"ITensorType.html#ITensors.diagITensor-Tuple{Type{<:Number}, QN, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.diagITensor","text":"diagITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]is)\ndiagITensor([::Type{ElT} = Float64, ][flux::QN = QN(), ]is::Index...)\n\nMake an ITensor with storage type NDTensors.DiagBlockSparse with elements zero(ElT). The ITensor only has diagonal blocks consistent with the specified flux.\n\nIf the element type is not specified, it defaults to Float64. If theflux is not specified, it defaults to QN().\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.delta-Tuple{Type{<:Number}, QN, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}","page":"ITensor","title":"ITensors.delta","text":"delta([::Type{ElT} = Float64, ][flux::QN = QN(), ]is)\ndelta([::Type{ElT} = Float64, ][flux::QN = QN(), ]is::Index...)\n\nMake an ITensor with storage type NDTensors.DiagBlockSparse with uniform elements one(ElT). The ITensor only has diagonal blocks consistent with the specified flux.\n\nIf the element type is not specified, it defaults to Float64. If theflux is not specified, it defaults to QN().\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Convert-to-Array","page":"ITensor","title":"Convert to Array","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"Array{ElT, N}(::ITensor, ::ITensors.Indices) where {ElT, N}\narray(::ITensor, ::Any...)\nmatrix(::ITensor, ::Any...)\nvector(::ITensor, ::Any...)\narray(::ITensor)\nmatrix(::ITensor)\nvector(::ITensor)","category":"page"},{"location":"ITensorType.html#Core.Array-Union{Tuple{N}, Tuple{ElT}, Tuple{ITensor, Union{Tuple{Vararg{IndexT}}, Vector{IndexT}} where IndexT<:Index}} where {ElT, N}","page":"ITensor","title":"Core.Array","text":"Array{ElT, N}(T::ITensor, i:Index...)\nArray{ElT}(T::ITensor, i:Index...)\nArray(T::ITensor, i:Index...)\n\nMatrix{ElT}(T::ITensor, row_i:Index, col_i::Index)\nMatrix(T::ITensor, row_i:Index, col_i::Index)\n\nVector{ElT}(T::ITensor)\nVector(T::ITensor)\n\nGiven an ITensor T with indices i..., returns an Array with a copy of the ITensor's elements. The order in which the indices are provided indicates the order of the data in the resulting Array.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.array-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"NDTensors.array","text":"array(T::ITensor, inds...)\n\nConvert an ITensor T to an Array.\n\nThe ordering of the elements in the Array are specified by the input indices inds. This tries to avoid copying of possible (i.e. may return a view of the original data), for example if the ITensor's storage is Dense and the indices are already in the specified ordering so that no permutation is required.\n\nwarning: Warning\nNote that in the future we may return specialized AbstractArray types for certain storage types, for example a LinearAlgebra.Diagonal type for an ITensor with Diag storage. The specific storage type shouldn't be relied upon.\n\nSee also matrix, vector.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.matrix-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"NDTensors.matrix","text":"matrix(T::ITensor, inds...)\n\nConvert an ITensor T to a Matrix.\n\nThe ordering of the elements in the Matrix are specified by the input indices inds. This tries to avoid copying of possible (i.e. may return a view of the original data), for example if the ITensor's storage is Dense and the indices are already in the specified ordering so that no permutation is required.\n\nwarning: Warning\nNote that in the future we may return specialized AbstractArray types for certain storage types, for example a LinearAlgebra.Diagonal type for an ITensor with Diag storage. The specific storage type shouldn't be relied upon.\n\nSee also array, vector.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.vector-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"NDTensors.vector","text":"vector(T::ITensor, inds...)\n\nConvert an ITensor T to an Vector.\n\nThe ordering of the elements in the Array are specified by the input indices inds. This tries to avoid copying of possible (i.e. may return a view of the original data), for example if the ITensor's storage is Dense and the indices are already in the specified ordering so that no permutation is required.\n\nwarning: Warning\nNote that in the future we may return specialized AbstractArray types for certain storage types, for example a LinearAlgebra.Diagonal type for an ITensor with Diag storage. The specific storage type shouldn't be relied upon.\n\nSee also array, matrix.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.array-Tuple{ITensor}","page":"ITensor","title":"NDTensors.array","text":"array(T::ITensor)\n\nGiven an ITensor T, returns an Array with a copy of the ITensor's elements, or a view in the case the the ITensor's storage is Dense.\n\nThe ordering of the elements in the Array, in terms of which Index is treated as the row versus column, depends on the internal layout of the ITensor.\n\nwarning: Warning\nThis method is intended for developer use only and not recommended for use in ITensor applications unless you know what you are doing (for example you are certain of the memory ordering of the ITensor because you permuted the indices into a certain order).\n\nSee also matrix, vector.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.matrix-Tuple{ITensor}","page":"ITensor","title":"NDTensors.matrix","text":"matrix(T::ITensor)\n\nGiven an ITensor T with two indices, returns a Matrix with a copy of the ITensor's elements, or a view in the case the ITensor's storage is Dense.\n\nThe ordering of the elements in the Matrix, in terms of which Index is treated as the row versus column, depends on the internal layout of the ITensor.\n\nwarning: Warning\nThis method is intended for developer use only and not recommended for use in ITensor applications unless you know what you are doing (for example you are certain of the memory ordering of the ITensor because you permuted the indices into a certain order).\n\nSee also array, vector.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.vector-Tuple{ITensor}","page":"ITensor","title":"NDTensors.vector","text":"vector(T::ITensor)\n\nGiven an ITensor T with one index, returns a Vector with a copy of the ITensor's elements, or a view in the case the ITensor's storage is Dense.\n\nSee also array, matrix.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Getting-and-setting-elements","page":"ITensor","title":"Getting and setting elements","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"getindex(::ITensor, ::Any...)\nsetindex!(::ITensor, ::Number, ::Int...)","category":"page"},{"location":"ITensorType.html#Base.getindex-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"Base.getindex","text":"getindex(T::ITensor, ivs...)\n\nGet the specified element of the ITensor, using a list of IndexVals or Pair{<:Index, Int}.\n\nExample\n\ni = Index(2; tags = \"i\")\nA = ITensor(2.0, i, i')\nA[i => 1, i' => 2] # 2.0, same as: A[i' => 2, i => 1]\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Base.setindex!-Tuple{ITensor, Number, Vararg{Int64}}","page":"ITensor","title":"Base.setindex!","text":"setindex!(T::ITensor, x::Number, ivs...)\n\nsetindex!(T::ITensor, x::Number, I::Integer...)\n\nsetindex!(T::ITensor, x::Number, I::CartesianIndex)\n\nSet the specified element of the ITensor, using a list of Pair{<:Index, Integer} (or IndexVal).\n\nIf just integers are used, set the specified element of the ITensor using internal Index ordering of the ITensor (only for advanced usage, only use if you know the axact ordering of the indices).\n\nExample\n\ni = Index(2; tags = \"i\")\nA = ITensor(i, i')\nA[i => 1, i' => 2] = 1.0 # same as: A[i' => 2, i => 1] = 1.0\nA[1, 2] = 1.0 # same as: A[i => 1, i' => 2] = 1.0\n\n# Some simple slicing is also supported\nA[i => 2, i' => :] = [2.0 3.0]\nA[2, :] = [2.0 3.0]\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Properties","page":"ITensor","title":"Properties","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"inds(::ITensor)\nind(::ITensor, ::Int)\ndir(::ITensor, ::Index)","category":"page"},{"location":"ITensorType.html#NDTensors.inds-Tuple{ITensor}","page":"ITensor","title":"NDTensors.inds","text":"inds(T::ITensor)\n\nReturn the indices of the ITensor as a Tuple.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.ind-Tuple{ITensor, Int64}","page":"ITensor","title":"NDTensors.ind","text":"ind(T::ITensor, i::Int)\n\nGet the Index of the ITensor along dimension i.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.dir-Tuple{ITensor, Index}","page":"ITensor","title":"ITensors.dir","text":"dir(A::ITensor, i::Index)\n\nReturn the direction of the Index i in the ITensor A.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Priming_and_tagging_ITensor","page":"ITensor","title":"Priming and tagging","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"prime(::ITensor, ::Any...)\nsetprime(::ITensor, ::Any...)\nnoprime(::ITensor, ::Any...)\nmapprime(::ITensor, ::Any...)\nswapprime(::ITensor, ::Any...)\naddtags(::ITensor, ::Any...)\nremovetags(::ITensor, ::Any...)\nreplacetags(::ITensor, ::Any...)\nsettags(::ITensor, ::Any...)\nswaptags(::ITensor, ::Any...)","category":"page"},{"location":"ITensorType.html#ITensors.prime-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.prime","text":"prime[!](A::ITensor, plinc::Int = 1; ) -> ITensor\n\nprime(inds, plinc::Int = 1; ) -> IndexSet\n\nIncrease the prime level of the indices of an ITensor or collection of indices.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.setprime-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.setprime","text":"setprime[!](A::ITensor, plev::Int; ) -> ITensor\n\nsetprime(inds, plev::Int; ) -> IndexSet\n\nSet the prime level of the indices of an ITensor or collection of indices.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.noprime-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.noprime","text":"noprime[!](A::ITensor; ) -> ITensor\n\nnoprime(inds; ) -> IndexSet\n\nSet the prime level of the indices of an ITensor or collection of indices to zero.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.mapprime-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.mapprime","text":"replaceprime[!](A::ITensor, plold::Int, plnew::Int; ) -> ITensor\nreplaceprime[!](A::ITensor, plold => plnew; ) -> ITensor\nmapprime[!](A::ITensor, ; ) -> ITensor\n\nreplaceprime(inds, plold::Int, plnew::Int; )\nreplaceprime(inds::IndexSet, plold => plnew; )\nmapprime(inds, ; )\n\nSet the prime level of the indices of an ITensor or collection of indices with prime level plold to plnew.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.swapprime-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.swapprime","text":"swapprime[!](A::ITensor, pl1::Int, pl2::Int; ) -> ITensor\nswapprime[!](A::ITensor, pl1 => pl2; ) -> ITensor\n\nswapprime(inds, pl1::Int, pl2::Int; )\nswapprime(inds, pl1 => pl2; )\n\nSet the prime level of the indices of an ITensor or collection of indices with prime level pl1 to pl2, and those with prime level pl2 to pl1.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.TagSets.addtags-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.TagSets.addtags","text":"addtags[!](A::ITensor, ts::String; ) -> ITensor\n\naddtags(inds, ts::String; )\n\nAdd the tags ts to the indices of an ITensor or collection of indices.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.TagSets.removetags-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.TagSets.removetags","text":"removetags[!](A::ITensor, ts::String; ) -> ITensor\n\nremovetags(inds, ts::String; )\n\nRemove the tags ts from the indices of an ITensor or collection of indices.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.TagSets.replacetags-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.TagSets.replacetags","text":"replacetags[!](A::ITensor, tsold::String, tsnew::String; ) -> ITensor\n\nreplacetags(is::IndexSet, tsold::String, tsnew::String; ) -> IndexSet\n\nReplace the tags tsold with tsnew for the indices of an ITensor.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.settags-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.settags","text":"settags[!](A::ITensor, ts::String; ) -> ITensor\n\nsettags(is::IndexSet, ts::String; ) -> IndexSet\n\nSet the tags of the indices of an ITensor or IndexSet to ts.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.swaptags-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.swaptags","text":"swaptags[!](A::ITensor, ts1::String, ts2::String; ) -> ITensor\n\nswaptags(is::IndexSet, ts1::String, ts2::String; ) -> IndexSet\n\nSwap the tags ts1 with ts2 for the indices of an ITensor.\n\nOptionally, only modify the indices with the specified keyword arguments.\n\nArguments\n\ntags = nothing: if specified, only modify Index i if hastags(i, tags) == true.\nplev = nothing: if specified, only modify Index i if hasplev(i, plev) == true.\n\nThe ITensor functions come in two versions, f and f!. The latter modifies the ITensor in-place. In both versions, the ITensor storage is not modified or copied (so it returns an ITensor with a view of the original storage).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Index-collections-set-operations","page":"ITensor","title":"Index collections set operations","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"commoninds\ncommonind\nuniqueinds\nuniqueind\nnoncommoninds\nnoncommonind\nunioninds\nunionind\nhascommoninds","category":"page"},{"location":"ITensorType.html#ITensors.commoninds","page":"ITensor","title":"ITensors.commoninds","text":"commoninds(A, B; kwargs...)\n\nReturn a Vector with indices that are common between the indices of A and B (the set intersection, similar to Base.intersect).\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.commonind","page":"ITensor","title":"ITensors.commonind","text":"commonind(A, B; kwargs...)\n\nReturn the first Index common between the indices of A and B.\n\nSee also commoninds.\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.uniqueinds","page":"ITensor","title":"ITensors.uniqueinds","text":"uniqueinds(A, B; kwargs...)\n\nReturn Vector with indices that are unique to the set of indices of A and not in B (the set difference, similar to Base.setdiff).\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.uniqueind","page":"ITensor","title":"ITensors.uniqueind","text":"uniqueind(A, B; kwargs...)\n\nReturn the first Index unique to the set of indices of A and not in B.\n\nSee also uniqueinds.\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.noncommoninds","page":"ITensor","title":"ITensors.noncommoninds","text":"noncommoninds(A, B; kwargs...)\n\nReturn a Vector with indices that are not common between the indices of A and B (the symmetric set difference, similar to Base.symdiff).\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.noncommonind","page":"ITensor","title":"ITensors.noncommonind","text":"noncommonind(A, B; kwargs...)\n\nReturn the first Index not common between the indices of A and B.\n\nSee also noncommoninds.\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.unioninds","page":"ITensor","title":"ITensors.unioninds","text":"unioninds(A, B; kwargs...)\n\nReturn a Vector with indices that are the union of the indices of A and B (the set union, similar to Base.union).\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.unionind","page":"ITensor","title":"ITensors.unionind","text":"unionind(A, B; kwargs...)\n\nReturn the first Index in the union of the indices of A and B.\n\nSee also unioninds.\n\nOptional keyword arguments:\n\ntags::String - a tag name or comma separated list of tag names that the returned indices must all have\nplev::Int - common prime level that the returned indices must all have\ninds - Index or collection of indices. Returned indices must come from this set of indices.\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#ITensors.hascommoninds","page":"ITensor","title":"ITensors.hascommoninds","text":"hascommoninds(A, B; kwargs...)\n\nhascommoninds(B; kwargs...) -> f::Function\n\nCheck if the ITensors or sets of indices A and B have common indices.\n\nIf only one ITensor or set of indices B is passed, return a function f such that f(A) = hascommoninds(A, B; kwargs...)\n\n\n\n\n\n","category":"function"},{"location":"ITensorType.html#Index-Manipulations","page":"ITensor","title":"Index Manipulations","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"replaceind(::ITensor, ::Any...)\nreplaceinds(::ITensor, ::Any...)\nswapind(::ITensor, ::Any...)\nswapinds(::ITensor, ::Any...)","category":"page"},{"location":"ITensorType.html#ITensors.replaceind-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.replaceind","text":"replaceind[!](A::ITensor, i1::Index, i2::Index) -> ITensor\n\nReplace the Index i1 with the Index i2 in the ITensor.\n\nThe indices must have the same space (i.e. the same dimension and QNs, if applicable).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.replaceinds-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.replaceinds","text":"replaceinds(A::ITensor, inds1, inds2) -> ITensor\n\nreplaceinds!(A::ITensor, inds1, inds2)\n\nReplace the Index inds1[n] with the Index inds2[n] in the ITensor, where n runs from 1 to length(inds1) == length(inds2).\n\nThe indices must have the same space (i.e. the same dimension and QNs, if applicable).\n\nThe storage of the ITensor is not modified or copied (the output ITensor is a view of the input ITensor).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.swapind-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.swapind","text":"swapind(A::ITensor, i1::Index, i2::Index) -> ITensor\n\nswapind!(A::ITensor, i1::Index, i2::Index)\n\nSwap the Index i1 with the Index i2 in the ITensor.\n\nThe indices must have the same space (i.e. the same dimension and QNs, if applicable).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.swapinds-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"ITensors.swapinds","text":"swapinds(A::ITensor, inds1, inds2) -> ITensor\n\nswapinds!(A::ITensor, inds1, inds2)\n\nSwap the Index inds1[n] with the Index inds2[n] in the ITensor, where n runs from 1 to length(inds1) == length(inds2).\n\nThe indices must have the same space (i.e. the same dimension and QNs, if applicable).\n\nThe storage of the ITensor is not modified or copied (the output ITensor is a view of the input ITensor).\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Math-operations","page":"ITensor","title":"Math operations","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"*(::ITensor, ::ITensor)\ndag(T::ITensor; kwargs...)\ndirectsum(::Pair{ITensor},::Pair{ITensor},::Pair{ITensor},args...; kws...)\nexp(::ITensor, ::Any, ::Any)\nnullspace(::ITensor, ::Any...)","category":"page"},{"location":"ITensorType.html#Base.:*-Tuple{ITensor, ITensor}","page":"ITensor","title":"Base.:*","text":"A::ITensor * B::ITensor\ncontract(A::ITensor, B::ITensor)\n\nContract ITensors A and B to obtain a new ITensor. This contraction * operator finds all matching indices common to A and B and sums over them, such that the result will have only the unique indices of A and B. To prevent indices from matching, their prime level or tags can be modified such that they no longer compare equal - for more information see the documentation on Index objects.\n\nExamples\n\ni = Index(2,\"index_i\"); j = Index(4,\"index_j\"); k = Index(3,\"index_k\")\n\nA = randomITensor(i,j)\nB = randomITensor(j,k)\nC = A * B # contract over Index j\n\nA = randomITensor(i,i')\nB = randomITensor(i,i'')\nC = A * B # contract over Index i\n\nA = randomITensor(i)\nB = randomITensor(j)\nC = A * B # outer product of A and B, no contraction\n\nA = randomITensor(i,j,k)\nB = randomITensor(k,i,j)\nC = A * B # inner product of A and B, all indices contracted\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.dag-Tuple{ITensor}","page":"ITensor","title":"ITensors.dag","text":"dag(T::ITensor; allow_alias = true)\n\nComplex conjugate the elements of the ITensor T and dagger the indices.\n\nBy default, an alias of the ITensor is returned (i.e. the output ITensor may share data with the input ITensor). If allow_alias = false, an alias is never returned.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#ITensors.directsum-Tuple{Pair{ITensor}, Pair{ITensor}, Pair{ITensor}, Vararg{Any}}","page":"ITensor","title":"ITensors.directsum","text":"directsum(A::Pair{ITensor}, B::Pair{ITensor}, ...; tags)\n\ndirectsum(output_inds, A::Pair{ITensor}, B::Pair{ITensor}, ...; tags)\n\nGiven a list of pairs of ITensors and indices, perform a partial direct sum of the tensors over the specified indices. Indices that are not specified to be summed must match between the tensors.\n\n(Note: Pair{ITensor} in Julia is short for Pair{ITensor,<:Any} which means any pair T => x where T is an ITensor.)\n\nIf all indices are specified then the operation is equivalent to creating a block diagonal tensor.\n\nReturns the ITensor representing the partial direct sum as well as the new direct summed indices. The tags of the direct summed indices are specified by the keyword arguments.\n\nOptionally, pass the new direct summed indices of the output tensor as the first argument (either a single Index or a collection), which must be proper direct sums of the input indices that are specified to be direct summed.\n\nSee Section 2.3 of https://arxiv.org/abs/1405.7786 for a definition of a partial direct sum of tensors.\n\nExamples\n\nx = Index(2, \"x\")\ni1 = Index(3, \"i1\")\nj1 = Index(4, \"j1\")\ni2 = Index(5, \"i2\")\nj2 = Index(6, \"j2\")\n\nA1 = randomITensor(x, i1)\nA2 = randomITensor(x, i2)\nS, s = directsum(A1 => i1, A2 => i2)\ndim(s) == dim(i1) + dim(i2)\n\ni1i2 = directsum(i1, i2)\nS = directsum(i1i2, A1 => i1, A2 => i2)\nhasind(S, i1i2)\n\nA3 = randomITensor(x, j1)\nS, s = directsum(A1 => i1, A2 => i2, A3 => j1)\ndim(s) == dim(i1) + dim(i2) + dim(j1)\n\nA1 = randomITensor(i1, x, j1)\nA2 = randomITensor(x, j2, i2)\nS, s = directsum(A1 => (i1, j1), A2 => (i2, j2); tags = [\"sum_i\", \"sum_j\"])\nlength(s) == 2\ndim(s[1]) == dim(i1) + dim(i2)\ndim(s[2]) == dim(j1) + dim(j2)\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Base.exp-Tuple{ITensor, Any, Any}","page":"ITensor","title":"Base.exp","text":"exp(A::ITensor, Linds=Rinds', Rinds=inds(A,plev=0); ishermitian = false)\n\nCompute the exponential of the tensor A by treating it as a matrix A_lr with the left index l running over all indices in Linds and r running over all indices in Rinds.\n\nOnly accepts index lists Linds,Rinds such that: (1) length(Linds) + length(Rinds) == length(inds(A)) (2) length(Linds) == length(Rinds) (3) For each pair of indices (Linds[n],Rinds[n]), Linds[n] and Rinds[n] represent the same Hilbert space (the same QN structure in the QN case, or just the same length in the dense case), and appear in A with opposite directions.\n\nWhen ishermitian=true the exponential of Hermitian(A_{lr}) is computed internally.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#LinearAlgebra.nullspace-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"LinearAlgebra.nullspace","text":"nullspace(T::ITensor, left_inds...; tags=\"n\", atol=1E-12, kwargs...)\n\nViewing the ITensor T as a matrix with the provided left_inds viewed as the row space and remaining indices viewed as the right indices or column space, the nullspace function computes the right null space. That is, it will return a tensor N acting on the right indices of T such that T*N is zero. The returned tensor N will also have a new index with the label \"n\" which indexes through the 'vectors' in the null space.\n\nFor example, if T has the indices i,j,k, calling N = nullspace(T,i,k) returns N with index j such that\n\n ___ ___\n i --| | | |\n | T |--j--| N |--n ≈ 0\n k --| | | |\n --- ---\n\nThe index n can be obtained by calling n = uniqueindex(N,T)\n\nNote that the implementation of this function is subject to change in the future, in which case the precise atol value that gives a certain null space size may change in future versions of ITensor.\n\nKeyword arguments:\n\natol::Float64=1E-12 - singular values of T†*T below this value define the null space\ntags::String=\"n\" - choose the tags of the index selecting elements of the null space\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Decompositions","page":"ITensor","title":"Decompositions","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"svd(::ITensor, ::Any...)\neigen(::ITensor, ::Any, ::Any)\nfactorize(::ITensor, ::Any...)","category":"page"},{"location":"ITensorType.html#LinearAlgebra.svd-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"LinearAlgebra.svd","text":"svd(A::ITensor, inds::Index...; )\n\nSingular value decomposition (SVD) of an ITensor A, computed by treating the \"left indices\" provided collectively as a row index, and the remaining \"right indices\" as a column index (matricization of a tensor).\n\nThe first three return arguments are U, S, and V, such that A ≈ U * S * V.\n\nWhether or not the SVD performs a trunction depends on the keyword arguments provided.\n\nIf the left or right set of indices are empty, all input indices are put on V or U respectively. To specify an empty set of left indices, you must explicitly use svd(A, ()) (svd(A) is currently undefined).\n\nExamples\n\nComputing the SVD of an order-three ITensor, such that the indices i and k end up on U and j ends up on V\n\ni = Index(2)\nj = Index(5)\nk = Index(2)\nA = randomITensor(i, j, k)\nU, S, V = svd(A, i, k);\n@show norm(A - U * S * V) <= 10 * eps() * norm(A)\n\nThe following code will truncate the last 2 singular values, since the total number of singular values is 4. The norm of the difference with the original tensor will be the sqrt root of the sum of the squares of the singular values that get truncated.\n\ntrunc, Strunc, Vtrunc = svd(A, i, k; maxdim=2);\n@show norm(A - Utrunc * Strunc * Vtrunc) ≈ sqrt(S[3, 3]^2 + S[4, 4]^2)\n\nAlternatively we can specify that we want to truncate the weights of the singular values up to a certain cutoff, so the total error will be no larger than the cutoff.\n\nUtrunc2, Strunc2, Vtrunc2 = svd(A, i, k; cutoff=1e-10);\n@show norm(A - Utrunc2 * Strunc2 * Vtrunc2) <= 1e-10\n\nKeywords\n\nmaxdim::Int: the maximum number of singular values to keep.\nmindim::Int: the minimum number of singular values to keep.\ncutoff::Float64: set the desired truncation error of the SVD, by default defined as the sum of the squares of the smallest singular values.\nlefttags::String = \"Link,u\": set the tags of the Index shared by U and S.\nrighttags::String = \"Link,v\": set the tags of the Index shared by S and V.\nalg::String = \"divide_and_conquer\". Options:\n\"divide_and_conquer\" - A divide-and-conquer algorithm. LAPACK's gesdd. Fast, but may lead to some innacurate singular values for very ill-conditioned matrices. Also may sometimes fail to converge, leading to errors (in which case \"qr_iteration\" or \"recursive\" can be tried).\n\"qr_iteration\" - Typically slower but more accurate for very ill-conditioned matrices compared to \"divide_and_conquer\". LAPACK's gesvd.\n\"recursive\" - ITensor's custom svd. Very reliable, but may be slow if high precision is needed. To get an svd of a matrix A, an eigendecomposition of A^dagger A is used to compute U and then a qr of A^dagger U is used to compute V. This is performed recursively to compute small singular values.\nuse_absolute_cutoff::Bool = false: set if all probability weights below the cutoff value should be discarded, rather than the sum of discarded weights.\nuse_relative_cutoff::Bool = true: set if the singular values should be normalized for the sake of truncation.\nmin_blockdim::Int = 0: for SVD of block-sparse or QN ITensors, require that the number of singular values kept be greater than or equal to this value when possible\n\nSee also: factorize, eigen\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#LinearAlgebra.eigen-Tuple{ITensor, Any, Any}","page":"ITensor","title":"LinearAlgebra.eigen","text":"eigen(A::ITensor[, Linds, Rinds]; )\n\nEigendecomposition of an ITensor A, computed by treating the \"left indices\" Linds provided collectively as a row index, and remaining \"right indices\" Rinds as a column index (matricization of a tensor).\n\nIf no indices are provided, pairs of primed and unprimed indices are searched for, with Linds taken to be the primed indices and Rinds taken to be the unprimed indices.\n\nThe return arguments are the eigenvalues D and eigenvectors U as tensors, such that A * U ∼ U * D (more precisely they are approximately equal up to proper replacements of indices, see the example for details).\n\nWhether or not eigen performs a trunction depends on the keyword arguments provided. Note that truncation is only well defined for positive semidefinite matrices.\n\nArguments\n\n- `maxdim::Int`: the maximum number of singular values to keep.\n- `mindim::Int`: the minimum number of singular values to keep.\n- `cutoff::Float64`: set the desired truncation error of the eigenvalues,\n by default defined as the sum of the squares of the smallest eigenvalues.\n For now truncation is only well defined for positive semi-definite\n eigenspectra.\n- `ishermitian::Bool = false`: specify if the matrix is Hermitian, in which\n case a specialized diagonalization routine will be used and it is\n guaranteed that real eigenvalues will be returned.\n- `plev::Int = 0`: set the prime level of the Indices of `D`. Default prime\n levels are subject to change.\n- `leftplev::Int = plev`: set the prime level of the Index unique to `D`.\n Default prime levels are subject to change.\n- `rightplev::Int = leftplev+1`: set the prime level of the Index shared\n by `D` and `U`. Default tags are subject to change.\n- `tags::String = \"Link,eigen\"`: set the tags of the Indices of `D`.\n Default tags are subject to change.\n- `lefttags::String = tags`: set the tags of the Index unique to `D`.\n Default tags are subject to change.\n- `righttags::String = tags`: set the tags of the Index shared by `D` and `U`.\n Default tags are subject to change.\n- `use_absolute_cutoff::Bool = false`: set if all probability weights below\n the `cutoff` value should be discarded, rather than the sum of discarded\n weights.\n- `use_relative_cutoff::Bool = true`: set if the singular values should\n be normalized for the sake of truncation.\n\nExamples\n\ni, j, k, l = Index(2, \"i\"), Index(2, \"j\"), Index(2, \"k\"), Index(2, \"l\")\nA = randomITensor(i, j, k, l)\nLinds = (i, k)\nRinds = (j, l)\nD, U = eigen(A, Linds, Rinds)\ndl, dr = uniqueind(D, U), commonind(D, U)\nUl = replaceinds(U, (Rinds..., dr) => (Linds..., dl))\nA * U ≈ Ul * D # true\n\nSee also: svd, factorize\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#LinearAlgebra.factorize-Tuple{ITensor, Vararg{Any}}","page":"ITensor","title":"LinearAlgebra.factorize","text":"factorize(A::ITensor, Linds::Index...; )\n\nPerform a factorization of A into ITensors L and R such that A ≈ L * R.\n\nArguments\n\northo::String = \"left\": Choose orthogonality properties of the factorization.\n\"left\": the left factor L is an orthogonal basis such that L * dag(prime(L, commonind(L,R))) ≈ I.\n\"right\": the right factor R forms an orthogonal basis.\n\"none\", neither of the factors form an orthogonal basis, and in general are made as symmetrically as possible (depending on the decomposition used).\nwhich_decomp::Union{String, Nothing} = nothing: choose what kind of decomposition is used.\nnothing: choose the decomposition automatically based on the other arguments. For example, when nothing is chosen and ortho = \"left\" or \"right\", and a cutoff is provided, svd or eigen is used depending on the provided cutoff (eigen is only used when the cutoff is greater than 1e-12, since it has a lower precision). When no truncation is requested qr is used for dense ITensors and svd for block-sparse ITensors (in the future qr will be used also for block-sparse ITensors in this case).\n\"svd\": L = U and R = S * V for ortho = \"left\", L = U * S and R = V for ortho = \"right\", and L = U * sqrt.(S) and R = sqrt.(S) * V for ortho = \"none\". To control which svd algorithm is choose, use the svd_alg keyword argument. See the documentation for svd for the supported algorithms, which are the same as those accepted by the alg keyword argument.\n\"eigen\": L = U and R = U^dagger A where U is determined from the eigendecompositon A A^dagger = U D U^dagger for ortho = \"left\" (and vice versa for ortho = \"right\"). \"eigen\" is not supported for ortho = \"none\".\n\"qr\": L=Q and R an upper-triangular matrix when ortho = \"left\", and R = Q and L a lower-triangular matrix when ortho = \"right\" (currently supported for dense ITensors only). In the future, other decompositions like QR (for block-sparse ITensors), polar, cholesky, LU, etc. are expected to be supported.\n\nFor truncation arguments, see: svd\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#Memory-operations","page":"ITensor","title":"Memory operations","text":"","category":"section"},{"location":"ITensorType.html","page":"ITensor","title":"ITensor","text":"permute(::ITensor, ::Any)\ndense(::ITensor)\ndenseblocks(::ITensor)","category":"page"},{"location":"ITensorType.html#ITensors.permute-Tuple{ITensor, Any}","page":"ITensor","title":"ITensors.permute","text":"permute(T::ITensor, inds...; allow_alias = false)\n\nReturn a new ITensor T with indices permuted according to the input indices inds. The storage of the ITensor is permuted accordingly.\n\nIf called with allow_alias = true, it avoids copying data if possible. Therefore, it may return an alias of the input ITensor (an ITensor that shares the same data), such as if the permutation turns out to be trivial.\n\nBy default, allow_alias = false, and it never returns an alias of the input ITensor.\n\nExamples\n\ni = Index(2, \"index_i\"); j = Index(4, \"index_j\"); k = Index(3, \"index_k\");\nT = randomITensor(i, j, k)\n\npT_1 = permute(T, k, i, j)\npT_2 = permute(T, j, i, k)\n\npT_noalias_1 = permute(T, i, j, k)\npT_noalias_1[1, 1, 1] = 12\nT[1, 1, 1] != pT_noalias_1[1, 1, 1]\n\npT_noalias_2 = permute(T, i, j, k; allow_alias = false)\npT_noalias_2[1, 1, 1] = 12\nT[1, 1, 1] != pT_noalias_1[1, 1, 1]\n\npT_alias = permute(T, i, j, k; allow_alias = true)\npT_alias[1, 1, 1] = 12\nT[1, 1, 1] == pT_alias[1, 1, 1]\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.dense-Tuple{ITensor}","page":"ITensor","title":"NDTensors.dense","text":"dense(T::ITensor)\n\nMake a new ITensor where the storage is the closest Dense storage, avoiding allocating new data if possible. For example, an ITensor with Diag storage will become Dense storage, filled with zeros except for the diagonal values.\n\n\n\n\n\n","category":"method"},{"location":"ITensorType.html#NDTensors.denseblocks-Tuple{ITensor}","page":"ITensor","title":"NDTensors.denseblocks","text":"denseblocks(T::ITensor)\n\nMake a new ITensor where any blocks which have a sparse format, such as diagonal sparsity, are made dense while still preserving the outer block-sparse structure. This method avoids allocating new data if possible.\n\nFor example, an ITensor with DiagBlockSparse storage will have BlockSparse storage afterwards.\n\n\n\n\n\n","category":"method"},{"location":"Sweeps.html#Sweeps","page":"Sweeps","title":"Sweeps","text":"","category":"section"},{"location":"Sweeps.html","page":"Sweeps","title":"Sweeps","text":"Sweeps\nSweeps(nsw::Int, d::AbstractMatrix)","category":"page"},{"location":"Sweeps.html#ITensors.ITensorMPS.Sweeps","page":"Sweeps","title":"ITensors.ITensorMPS.Sweeps","text":"A Sweeps objects holds information about the various parameters controlling a density matrix renormalization group (DMRG) or similar matrix product state (MPS) calculation.\n\nFor a Sweeps object sw the available parameters are:\n\nnsweep(sw) – the number of sweeps to do\nmaxdim(sw,n) – maximum MPS bond dimension for sweep n\nmindim(sw,n) – minimum MPS bond dimension for sweep n\ncutoff(sw,n) – truncation error cutoff for sweep n\nnoise(sw,n) – noise term coefficient for sweep n\n\n\n\n\n\n","category":"type"},{"location":"Sweeps.html#ITensors.ITensorMPS.Sweeps-Tuple{Int64, AbstractMatrix}","page":"Sweeps","title":"ITensors.ITensorMPS.Sweeps","text":"Sweeps(d::AbstractMatrix)\n\nSweeps(nsweep::Int, d::AbstractMatrix)\n\nMake a sweeps object from a matrix of input values. The first row should be strings that define which variables are being set (\"maxdim\", \"cutoff\", \"mindim\", and \"noise\").\n\nIf the number of sweeps are not specified, they are determined from the size of the input matrix.\n\nExamples\n\njulia > Sweeps(\n [\n \"maxdim\" \"mindim\" \"cutoff\" \"noise\"\n 50 10 1e-12 1E-7\n 100 20 1e-12 1E-8\n 200 20 1e-12 1E-10\n 400 20 1e-12 0\n 800 20 1e-12 1E-11\n 800 20 1e-12 0\n ],\n)\nSweeps\n1cutoff = 1.0E-12, maxdim = 50, mindim = 10, noise = 1.0E-07\n2cutoff = 1.0E-12, maxdim = 100, mindim = 20, noise = 1.0E-08\n3cutoff = 1.0E-12, maxdim = 200, mindim = 20, noise = 1.0E-10\n4cutoff = 1.0E-12, maxdim = 400, mindim = 20, noise = 0.0E+00\n5cutoff = 1.0E-12, maxdim = 800, mindim = 20, noise = 1.0E-11\n6cutoff = 1.0E-12, maxdim = 800, mindim = 20, noise = 0.0E+00\n\n\n\n\n\n","category":"method"},{"location":"Sweeps.html#Modifying-Sweeps-Objects","page":"Sweeps","title":"Modifying Sweeps Objects","text":"","category":"section"},{"location":"Sweeps.html","page":"Sweeps","title":"Sweeps","text":"setmaxdim!\nsetcutoff!\nsetnoise!\nsetmindim!","category":"page"},{"location":"Sweeps.html#ITensors.ITensorMPS.setmaxdim!","page":"Sweeps","title":"ITensors.ITensorMPS.setmaxdim!","text":"maxdim!(sw::Sweeps,maxdims::Int...)\n\nSet the maximum MPS bond dimension for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.\n\n\n\n\n\n","category":"function"},{"location":"Sweeps.html#ITensors.ITensorMPS.setcutoff!","page":"Sweeps","title":"ITensors.ITensorMPS.setcutoff!","text":"cutoff!(sw::Sweeps,maxdims::Int...)\n\nSet the MPS truncation error used for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.\n\n\n\n\n\n","category":"function"},{"location":"Sweeps.html#ITensors.ITensorMPS.setnoise!","page":"Sweeps","title":"ITensors.ITensorMPS.setnoise!","text":"noise!(sw::Sweeps,maxdims::Int...)\n\nSet the noise-term coefficient used for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.\n\n\n\n\n\n","category":"function"},{"location":"Sweeps.html#ITensors.ITensorMPS.setmindim!","page":"Sweeps","title":"ITensors.ITensorMPS.setmindim!","text":"mindim!(sw::Sweeps,maxdims::Int...)\n\nSet the minimum MPS bond dimension for each sweep by providing up to nsweep(sw) values. If fewer values are provided, the last value is repeated for the remaining sweeps.\n\n\n\n\n\n","category":"function"},{"location":"Sweeps.html#Getting-Sweeps-Object-Data","page":"Sweeps","title":"Getting Sweeps Object Data","text":"","category":"section"},{"location":"Sweeps.html","page":"Sweeps","title":"Sweeps","text":"nsweep(sw::Sweeps)\nmaxdim(sw::Sweeps,n::Int)\ncutoff(sw::Sweeps,n::Int)\nnoise(sw::Sweeps,n::Int)\nmindim(sw::Sweeps,n::Int)","category":"page"},{"location":"Sweeps.html#ITensors.ITensorMPS.nsweep-Tuple{Sweeps}","page":"Sweeps","title":"ITensors.ITensorMPS.nsweep","text":"nsweep(sw::Sweeps)\nlength(sw::Sweeps)\n\nObtain the number of sweeps parameterized by this sweeps object.\n\n\n\n\n\n","category":"method"},{"location":"Sweeps.html#NDTensors.maxdim-Tuple{Sweeps, Int64}","page":"Sweeps","title":"NDTensors.maxdim","text":"maxdim(sw::Sweeps,n::Int)\n\nMaximum MPS bond dimension allowed by the Sweeps object sw during sweep n\n\n\n\n\n\n","category":"method"},{"location":"Sweeps.html#ITensors.ITensorMPS.cutoff-Tuple{Sweeps, Int64}","page":"Sweeps","title":"ITensors.ITensorMPS.cutoff","text":"cutoff(sw::Sweeps,n::Int)\n\nTruncation error cutoff setting of the Sweeps object sw during sweep n\n\n\n\n\n\n","category":"method"},{"location":"Sweeps.html#ITensors.ITensorMPS.noise-Tuple{Sweeps, Int64}","page":"Sweeps","title":"ITensors.ITensorMPS.noise","text":"noise(sw::Sweeps,n::Int)\n\nNoise term coefficient setting of the Sweeps object sw during sweep n\n\n\n\n\n\n","category":"method"},{"location":"Sweeps.html#NDTensors.mindim-Tuple{Sweeps, Int64}","page":"Sweeps","title":"NDTensors.mindim","text":"mindim(sw::Sweeps,n::Int)\n\nMinimum MPS bond dimension allowed by the Sweeps object sw during sweep n\n\n\n\n\n\n","category":"method"},{"location":"DeveloperGuide.html#Developer-Guide","page":"Developer Guide","title":"Developer Guide","text":"","category":"section"},{"location":"DeveloperGuide.html#Keyword-Argument-Best-Practices","page":"Developer Guide","title":"Keyword Argument Best Practices","text":"","category":"section"},{"location":"DeveloperGuide.html","page":"Developer Guide","title":"Developer Guide","text":"Keyword arguments such as f(x,y; a=1, b=2) are a powerful Julia feature, but it is easy to misuse them in library code. Below are the \"best practices\" for using keyword arguments when developing ITensor library code.","category":"page"},{"location":"DeveloperGuide.html","page":"Developer Guide","title":"Developer Guide","text":"A particular challenge how to properly use keyword argument \"forwarding\" where the notation f(; a, b, kwargs...) allows any number of keyword arguments to be passed. If a keyword argument is misspelled, then forwarding keywords with kwargs... will silently allow the misspelling, whereas ideally there would be an error message.","category":"page"},{"location":"DeveloperGuide.html","page":"Developer Guide","title":"Developer Guide","text":"Best practices:","category":"page"},{"location":"DeveloperGuide.html","page":"Developer Guide","title":"Developer Guide","text":"Popping Terminal Keyword Arguments: When passing keyword arguments downward through a stack of function calls, if a certain keyword argument will not be used in any functions further down the stack, then these arguments should be listed explicitly to remove them from the keyword arguments.\nFor example, in a call stack fA -> fB -> fC if a keyword argument such as cutoff is used in the body of fB but not in fC, then use the following pattern:\nfunction fA(...; kwargs...)\n ...\n fB(...; kwargs...)\n ...\nend\n\nfunction fB(...; cutoff, kwargs...) # <- explicitly list cutoff here\n ...\n truncate!(psi; cutoff) # <- fB uses cutoff\n fC(...; kwargs...) # fC does not get passed cutoff\nend\n\nfunction fC(...; maxdim, outputlevel) # fC does not use or need the `cutoff` kwarg\n ...\nend\nLeaf Functions Should Not Take kwargs...: Functions which are the last in the call stack to take any keyword arguments should not take keyword arguments by the kwargs... pattern. They should only take an explicit list of keyword arguments, so as to ensure that an error is thrown if a keyword argument is misspelled or missing (if it has no default value).\nExample: fC above is a leaf function and does not have kwargs... in its signature.\nUse Functions to Set Defaults: Keyword arguments can be made optional by providing default values. To avoid having explicit and possibly inconsistent defaults spread all over the library code, use globally defined functions to provide these defaults.\nFor example:\nfunction sum(A::MPS, B::MPS; cutoff=default_cutoff(), kwargs...)\n...\nend\n\nfunction inner(A::MPS, B::MPS; cutoff=default_cutoff(), kwargs...)\n...\nend\nwhere above the default value for the cutoff keyword is provided by a function default_cutoff() that is defined for the whole library.\nUse Named Tuples to \"Tunnel\" Keywords to Leaf Functions: This is a more advanced pattern. In certain situations, there might be multiple leaf functions depending on the execution pathway of the code or in cases where the leaf function is a \"callback\" passed into the code from the upper-level calling code.\nIn such cases, different leaf function implementations may expect different sets of keyword arguments.\nTo avoid requiring all leaf functions to take all possible keyword arguments (or to use the kwargs... pattern as a workaround, breaking rule #2 above), use the following pattern:\nfunction fA(callback, psi; callback_args, kwargs...)\n ...\n callback(psi; callback_args...)\n ...\nend\n\nmy_callback(psi; a, b) = ... # define custom callback function\n\n# Call fA like this:\nfA(my_callback, psi; callback_args = (; a, b))\n\nExternal (non-ITensor) Functions: Though it requires judgment in each case, if the keyword arguments an external (non-ITensor) function accepts are small in number, not expected to change, and known ahead of time, try to list them explicitly if possible (rather than forwarding with kwargs...). Possible exceptions could be if you want to make use of defaults defined for keyword arguments of an external function.","category":"page"},{"location":"QNTricks.html#Symmetric-(QN-Conserving)-Tensors:-Background-and-Usage","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN Conserving) Tensors: Background and Usage","text":"","category":"section"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"Here is a collection of background material and example codes for understanding how symmetric tensors (tensors with conserved quantum numbers) work in ITensors.jl","category":"page"},{"location":"QNTricks.html#Combiners-and-Symmetric-Tensors","page":"Symmetric (QN conserving) tensors: background and usage","title":"Combiners and Symmetric Tensors","text":"","category":"section"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"In ITensors.jl, combiners are special sparse tensors that represent the action of taking the tensor product of one or more indices. It generalizes the idea of reshaping and permuting. For dense ITensors, a combiner is just the action of permuting and reshaping the data of the tensor. For symmetric tensors (quantum number conserving tensors represented as block sparse tensors), the combiner also fuses symmetry sectors together. They can be used for various purposes. Generally they are used internally in the library, for example in order to reshape a high order ITensor into an order 2 ITensor to perform a matrix decomposition like an SVD or eigendecomposition.","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"For example:","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"using ITensors\n\n# This is a short code showing how a combiner\n# can be used to \"flip\" the direction of an Index\ni = Index([QN(0) => 2, QN(1) => 3], \"i\")\nj = Index([QN(0) => 2, QN(1) => 3], \"j\")\nA = randomITensor(i, dag(j))\nC = combiner(i, dag(j); tags = \"c\", dir = dir(i))\ninds(A)\ninds(A * C)","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"You can see that the combiner reshapes the indices of A into a single Index that contains the tensor product of the two input spaces. The spaces have size QN(-1) => 2 * 3, QN(0) => 2 * 2 + 3 * 3, and QN(0) => 2 * 3 (determined from all of the combinations of combining the sectors of the different indices, where the QNs are added and the block dimensions are multiplied). The ordering of the sectors is determined internally by ITensors.jl.","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"You can also use a combiner on a single Index, which can be helpful for changing the direction of an Index or combining multiple sectors of the same symmetry into a single sector:","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"using ITensors\n\n# This is a short code showing how a combiner\n# can be used to \"flip\" the direction of an Index\ni = Index([QN(0) => 2, QN(1) => 3], \"i\")\nj = dag(Index([QN(0) => 2, QN(1) => 3], \"j\"))\nA = randomITensor(i, j)\nC = combiner(j; tags = \"jflip\", dir = -dir(j))\ninds(A)\ninds(A * C)","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"Unless you are writing very specialized custom code with symmetric tensors, this is generally not needed.","category":"page"},{"location":"QNTricks.html#Block-Sparsity-and-Quantum-Numbers","page":"Symmetric (QN conserving) tensors: background and usage","title":"Block Sparsity and Quantum Numbers","text":"","category":"section"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"In general, not all blocks that are allowed according to the flux will actually exist in the tensor (which helps in many cases for efficiency). Usually this would happen when the tensor is first constructed and not all blocks are explicitly set:","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"using ITensors\n\ni = Index([QN(0) => 1, QN(1) => 1])\nA = ITensor(i', dag(i));\nA[2, 2] = 1.0;\n@show A;\nD, U = eigen(A; ishermitian=true);\n@show D;\n@show U;","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"If we had set A[1, 1] = 0.0 as well, then all of the allowed blocks (according to the flux QN(0) would exist and would be included in the eigendecomposition:","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"using ITensors\n\ni = Index([QN(0) => 1, QN(1) => 1])\nA = ITensor(i', dag(i));\nA[2, 2] = 1.0;\nA[1, 1] = 0.0;\n@show A;\nD, U = eigen(A; ishermitian=true);\n@show D;\n@show U;","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"\"Missing\" blocks can also occur with tensor contractions, since the final blocks of the output tensor are made from combinations of contractions of blocks from the input tensors, and there is no guarantee that all flux-consistent blocks will end up in the result:","category":"page"},{"location":"QNTricks.html","page":"Symmetric (QN conserving) tensors: background and usage","title":"Symmetric (QN conserving) tensors: background and usage","text":"using ITensors\n\ni = Index([QN(0) => 1, QN(1) => 1])\nj = Index([QN(0) => 1])\nA = ITensor(i, dag(j));\nA[2, 1] = 1.0;\n@show A;\nA2 = prime(A, i) * dag(A);\n@show A2;\nD, U = eigen(A2; ishermitian=true);\n@show D;\n@show U;","category":"page"},{"location":"getting_started/DebugChecks.html#Enabling-Debug-Checks","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"","category":"section"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"ITensor provides some optional checks for common errors, which we call \"debug checks\". These can be enabled with the command:","category":"page"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"ITensors.enable_debug_checks()","category":"page"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"and disabled with the command:","category":"page"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"ITensors.disable_debug_checks()","category":"page"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"We recommend enabling debug checks when you are developing and testing your code, and then disabling them when running in production to get the best performance.","category":"page"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"For example, when debug checks are turned on, ITensor checks that all indices of an ITensor are unique (if they are not unique, it leads to undefined behavior in tensor operations like contraction, addition, and decomposition):","category":"page"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"julia> using ITensors\n\njulia> i = Index(2)\n(dim=2|id=913)\n\njulia> A = randomITensor(i', i)\nITensor ord=2 (dim=2|id=913)' (dim=2|id=913)\nNDTensors.Dense{Float64, Vector{Float64}}\n\njulia> noprime(A)\nITensor ord=2 (dim=2|id=913) (dim=2|id=913)\nNDTensors.Dense{Float64, Vector{Float64}}\n\njulia> ITensors.enable_debug_checks()\nusing_debug_checks (generic function with 1 method)\n\njulia> noprime(A)\nERROR: Trying to create ITensors with collection of indices ((dim=2|id=913), (dim=2|id=913)). Indices must be unique.\nStacktrace:\n [1] error(s::String)\n @ Base ./error.jl:33\n [2] macro expansion\n @ ~/.julia/packages/ITensors/cu9Bo/src/itensor.jl:85 [inlined]\n [3] macro expansion\n @ ~/.julia/packages/ITensors/cu9Bo/src/global_variables.jl:177 [inlined]\n [4] ITensor\n @ ~/.julia/packages/ITensors/cu9Bo/src/itensor.jl:82 [inlined]\n [5] #itensor#123\n @ ~/.julia/packages/ITensors/cu9Bo/src/itensor.jl:123 [inlined]\n [6] itensor(args::NDTensors.DenseTensor{Float64, 2, Tuple{Index{Int64}, Index{Int64}}, NDTensors.Dense{Float64, Vector{Float64}}})\n @ ITensors ~/.julia/packages/ITensors/cu9Bo/src/itensor.jl:123\n [7] noprime(::ITensor; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})\n @ ITensors ~/.julia/packages/ITensors/cu9Bo/src/itensor.jl:1211\n [8] noprime(::ITensor)\n @ ITensors ~/.julia/packages/ITensors/cu9Bo/src/itensor.jl:1211\n [9] top-level scope\n @ REPL[7]:1","category":"page"},{"location":"getting_started/DebugChecks.html","page":"Enabling Debug Checks","title":"Enabling Debug Checks","text":"You can track where debug checks are located in the code here, and add your own debug checks to your own code by wrapping your code with the macro ITensors.@debug_check.","category":"page"},{"location":"IndexType.html#Index","page":"Index","title":"Index","text":"","category":"section"},{"location":"IndexType.html#Description","page":"Index","title":"Description","text":"","category":"section"},{"location":"IndexType.html","page":"Index","title":"Index","text":"Index\nITensors.QNIndex","category":"page"},{"location":"IndexType.html#ITensors.Index","page":"Index","title":"ITensors.Index","text":"An Index represents a single tensor index with fixed dimension dim. Copies of an Index compare equal unless their tags are different.\n\nAn Index carries a TagSet, a set of tags which are small strings that specify properties of the Index to help distinguish it from other Indices. There is a special tag which is referred to as the integer tag or prime level which can be incremented or decremented with special priming functions.\n\nInternally, an Index has a fixed id number, which is how the ITensor library knows two indices are copies of a single original Index. Index objects must have the same id, as well as the tags to compare equal.\n\n\n\n\n\n","category":"type"},{"location":"IndexType.html#ITensors.QNIndex","page":"Index","title":"ITensors.QNIndex","text":"A QN Index is an Index with QN block storage instead of just an integer dimension. The QN block storage is a vector of pairs of QNs and block dimensions. The total dimension of a QN Index is the sum of the dimensions of the blocks of the Index.\n\n\n\n\n\n","category":"type"},{"location":"IndexType.html#Constructors","page":"Index","title":"Constructors","text":"","category":"section"},{"location":"IndexType.html","page":"Index","title":"Index","text":"Index(::Int)\nIndex(::Int, ::Union{AbstractString, TagSet})\nIndex(::Pair{QN, Int}...)\nIndex(::Vector{Pair{QN, Int}})\nIndex(::Vector{Pair{QN, Int}}, ::Union{AbstractString, TagSet})","category":"page"},{"location":"IndexType.html#ITensors.Index-Tuple{Int64}","page":"Index","title":"ITensors.Index","text":"Index(dim::Int; tags::Union{AbstractString, TagSet} = \"\",\n plev::Int = 0)\n\nCreate an Index with a unique id, a TagSet given by tags, and a prime level plev.\n\nExamples\n\njulia> i = Index(2; tags=\"l\", plev=1)\n(dim=2|id=818|\"l\")'\n\njulia> dim(i)\n2\n\njulia> plev(i)\n1\n\njulia> tags(i)\n\"l\"\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.Index-Tuple{Int64, Union{ITensors.TagSets.GenericTagSet{BitIntegers.UInt256, 4}, AbstractString}}","page":"Index","title":"ITensors.Index","text":"Index(dim::Integer, tags::Union{AbstractString, TagSet}; plev::Int = 0)\n\nCreate an Index with a unique id and a tagset given by tags.\n\nExamples\n\njulia> i = Index(2, \"l,tag\")\n(dim=2|id=58|\"l,tag\")\n\njulia> dim(i)\n2\n\njulia> plev(i)\n0\n\njulia> tags(i)\n\"l,tag\"\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.Index-Tuple{Vararg{Pair{QN, Int64}}}","page":"Index","title":"ITensors.Index","text":"Index(qnblocks::Pair{QN, Int64}...; dir::Arrow = Out,\n tags = \"\",\n plev::Integer = 0)\n\nConstruct a QN Index from a list of pairs of QN and block dimensions.\n\nExample\n\nIndex(QN(\"Sz\", -1) => 1, QN(\"Sz\", 1) => 1; tags = \"i\")\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.Index-Tuple{Vector{Pair{QN, Int64}}}","page":"Index","title":"ITensors.Index","text":"Index(qnblocks::Vector{Pair{QN, Int64}}; dir::Arrow = Out,\n tags = \"\",\n plev::Integer = 0)\n\nConstruct a QN Index from a Vector of pairs of QN and block dimensions.\n\nNote: in the future, this may enforce that all blocks have the same QNs (which would allow for some optimizations, for example when constructing random QN ITensors).\n\nExample\n\nIndex([QN(\"Sz\", -1) => 1, QN(\"Sz\", 1) => 1]; tags = \"i\")\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.Index-Tuple{Vector{Pair{QN, Int64}}, Union{ITensors.TagSets.GenericTagSet{BitIntegers.UInt256, 4}, AbstractString}}","page":"Index","title":"ITensors.Index","text":"Index(qnblocks::Vector{Pair{QN, Int64}}, tags; dir::Arrow = Out,\n plev::Integer = 0)\n\nConstruct a QN Index from a Vector of pairs of QN and block dimensions.\n\nExample\n\nIndex([QN(\"Sz\", -1) => 1, QN(\"Sz\", 1) => 1], \"i\"; dir = In)\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#Properties","page":"Index","title":"Properties","text":"","category":"section"},{"location":"IndexType.html","page":"Index","title":"Index","text":"id(::Index)\nhasid(::Index, ::ITensors.IDType)\ntags(::Index)\nITensors.set_strict_tags!(::Bool)\nITensors.using_strict_tags()\nhastags(::Index, ::Union{AbstractString,TagSet})\nplev(::Index)\nhasplev(::Index, ::Int)\ndim(::Index)\n==(::Index, ::Index)\ndir(::Index)\nhasqns(::Index)","category":"page"},{"location":"IndexType.html#ITensors.id-Tuple{Index}","page":"Index","title":"ITensors.id","text":"id(i::Index)\n\nObtain the id of an Index, which is a unique 64 digit integer.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.hasid-Tuple{Index, UInt64}","page":"Index","title":"ITensors.hasid","text":"hasid(i::Index, id::ITensors.IDType)\n\nCheck if an Index i has the provided id.\n\nExamples\n\njulia> i = Index(2)\n(dim=2|id=321)\n\njulia> hasid(i, id(i))\ntrue\n\njulia> j = Index(2)\n(dim=2|id=17)\n\njulia> hasid(i, id(j))\nfalse\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.tags-Tuple{Index}","page":"Index","title":"ITensors.tags","text":"tags(i::Index)\n\nObtain the TagSet of an Index.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.TagSets.set_strict_tags!-Tuple{Bool}","page":"Index","title":"ITensors.TagSets.set_strict_tags!","text":"set_strict_tags!(enable::Bool) -> Bool\n\n\nEnable or disable checking for overflow of the number of tags of a TagSet or the number of characters of a tag. If enabled (set to true), an error will be thrown if overflow occurs, otherwise the overflow will be ignored and the extra tags or tag characters will be dropped. This could cause unexpected bugs if tags are being used to distinguish Index objects that have the same ids and prime levels, but that is generally discouraged and should only be used if you know what you are doing.\n\nSee also ITensors.using_strict_tags.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.TagSets.using_strict_tags-Tuple{}","page":"Index","title":"ITensors.TagSets.using_strict_tags","text":"using_strict_tags() -> Bool\n\n\nSee if checking for overflow of the number of tags of a TagSet or the number of characters of a tag is enabled or disabled.\n\nSee also ITensors.set_strict_tags!.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.TagSets.hastags-Tuple{Index, Union{ITensors.TagSets.GenericTagSet{BitIntegers.UInt256, 4}, AbstractString}}","page":"Index","title":"ITensors.TagSets.hastags","text":"hastags(i::Index, ts::Union{AbstractString,TagSet})\n\nCheck if an Index i has the provided tags, which can be a string of comma-separated tags or a TagSet object.\n\nExamples\n\njulia> i = Index(2, \"SpinHalf,Site,n=3\")\n(dim=2|id=861|\"Site,SpinHalf,n=3\")\n\njulia> hastags(i, \"SpinHalf,Site\")\ntrue\n\njulia> hastags(i, \"Link\")\nfalse\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.plev-Tuple{Index}","page":"Index","title":"ITensors.plev","text":"plev(i::Index)\n\nObtain the prime level of an Index.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.hasplev-Tuple{Index, Int64}","page":"Index","title":"ITensors.hasplev","text":"hasplev(i::Index, plev::Int)\n\nCheck if an Index i has the provided prime level.\n\nExamples\n\njulia> i = Index(2; plev=2)\n(dim=2|id=543)''\n\njulia> hasplev(i, 2)\ntrue\n\njulia> hasplev(i, 1)\nfalse\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#NDTensors.dim-Tuple{Index}","page":"Index","title":"NDTensors.dim","text":"dim(i::Index)\n\nObtain the dimension of an Index.\n\nFor a QN Index, this is the sum of the block dimensions.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#Base.:==-Tuple{Index, Index}","page":"Index","title":"Base.:==","text":"==(i1::Index, i1::Index)\n\nCompare indices for equality. First the id's are compared, then the prime levels are compared, and finally the tags are compared.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.dir-Tuple{Index}","page":"Index","title":"ITensors.dir","text":"dir(i::Index)\n\nReturn the direction of an Index (ITensors.In, ITensors.Out, or ITensors.Neither).\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.hasqns-Tuple{Index}","page":"Index","title":"ITensors.hasqns","text":"hasqns(::Index)\n\nChecks of the Index has QNs or not.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#Priming-and-tagging-methods","page":"Index","title":"Priming and tagging methods","text":"","category":"section"},{"location":"IndexType.html","page":"Index","title":"Index","text":"prime(::Index, ::Int)\nadjoint(::Index)\n^(::Index, ::Int)\nsetprime(::Index, ::Int)\nnoprime(::Index)\nsettags(::Index, ::Any)\naddtags(::Index, ::Any)\nremovetags(::Index, ::Any)\nreplacetags(::Index, ::Any, ::Any)","category":"page"},{"location":"IndexType.html#ITensors.prime-Tuple{Index, Int64}","page":"Index","title":"ITensors.prime","text":"prime(i::Index, plinc::Int = 1)\n\nReturn a copy of Index i with its prime level incremented by the amount plinc\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#Base.adjoint-Tuple{Index}","page":"Index","title":"Base.adjoint","text":"adjoint(i::Index)\n\nPrime an Index using the notation i'.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#Base.:^-Tuple{Index, Int64}","page":"Index","title":"Base.:^","text":"^(i::Index, pl::Int)\n\nPrime an Index using the notation i^3.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.setprime-Tuple{Index, Int64}","page":"Index","title":"ITensors.setprime","text":"setprime(i::Index, plev::Int)\n\nReturn a copy of Index i with its prime level set to plev\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.noprime-Tuple{Index}","page":"Index","title":"ITensors.noprime","text":"noprime(i::Index)\n\nReturn a copy of Index i with its prime level set to zero.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.settags-Tuple{Index, Any}","page":"Index","title":"ITensors.settags","text":"settags(i::Index, ts)\n\nReturn a copy of Index i with tags replaced by the ones given The ts argument can be a comma-separated string of tags or a TagSet.\n\nExamples\n\njulia> i = Index(2, \"SpinHalf,Site,n=3\")\n(dim=2|id=543|\"Site,SpinHalf,n=3\")\n\njulia> hastags(i, \"Link\")\nfalse\n\njulia> j = settags(i, \"Link,n=4\")\n(dim=2|id=543|\"Link,n=4\")\n\njulia> hastags(j, \"Link\")\ntrue\n\njulia> hastags(j, \"n=4,Link\")\ntrue\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.TagSets.addtags-Tuple{Index, Any}","page":"Index","title":"ITensors.TagSets.addtags","text":"addtags(i::Index,ts)\n\nReturn a copy of Index i with the specified tags added to the existing ones. The ts argument can be a comma-separated string of tags or a TagSet.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.TagSets.removetags-Tuple{Index, Any}","page":"Index","title":"ITensors.TagSets.removetags","text":"removetags(i::Index, ts)\n\nReturn a copy of Index i with the specified tags removed. The ts argument can be a comma-separated string of tags or a TagSet.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.TagSets.replacetags-Tuple{Index, Any, Any}","page":"Index","title":"ITensors.TagSets.replacetags","text":"replacetags(i::Index, tsold, tsnew)\n\nreplacetags(i::Index, tsold => tsnew)\n\nIf the tag set of i contains the tags specified by tsold, replaces these with the tags specified by tsnew, preserving any other tags. The arguments tsold and tsnew can be comma-separated strings of tags, or TagSet objects.\n\nExamples\n\njulia> i = Index(2; tags=\"l,x\", plev=1)\n(dim=2|id=83|\"l,x\")'\n\njulia> replacetags(i, \"l\", \"m\")\n(dim=2|id=83|\"m,x\")'\n\njulia> replacetags(i, \"l\" => \"m\")\n(dim=2|id=83|\"m,x\")'\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#Methods","page":"Index","title":"Methods","text":"","category":"section"},{"location":"IndexType.html","page":"Index","title":"Index","text":"sim(::Index)\ndag(::Index)\nremoveqns(::Index)","category":"page"},{"location":"IndexType.html#NDTensors.sim-Tuple{Index}","page":"Index","title":"NDTensors.sim","text":"sim(i::Index; tags = tags(i), plev = plev(i), dir = dir(i))\n\nProduces an Index with the same properties (dimension or QN structure) but with a new id.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.dag-Tuple{Index}","page":"Index","title":"ITensors.dag","text":"dag(i::Index)\n\nCopy an index i and reverse its direction.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.removeqns-Tuple{Index}","page":"Index","title":"ITensors.removeqns","text":"removeqns(::Index)\n\nRemoves the QNs from the Index, if it has any.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#Iterating","page":"Index","title":"Iterating","text":"","category":"section"},{"location":"IndexType.html","page":"Index","title":"Index","text":"eachval(::Index)\neachindval(::Index)","category":"page"},{"location":"IndexType.html#ITensors.eachval-Tuple{Index}","page":"Index","title":"ITensors.eachval","text":"eachval(i::Index)\n\nCreate an iterator whose values range over the dimension of the provided Index.\n\n\n\n\n\n","category":"method"},{"location":"IndexType.html#ITensors.eachindval-Tuple{Index}","page":"Index","title":"ITensors.eachindval","text":"eachindval(i::Index)\n\nCreate an iterator whose values are Pairs of the form i=>n with n from 1:dim(i). This iterator is useful for accessing elements of an ITensor in a loop without needing to know the ordering of the indices. See also eachindval(is::Index...).\n\n\n\n\n\n","category":"method"},{"location":"UpgradeGuide_0.1_to_0.2.html#Upgrade-guide","page":"Upgrading from 0.1 to 0.2","title":"Upgrade guide","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html#Upgrading-from-ITensors.jl-0.1-to-0.2","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from ITensors.jl 0.1 to 0.2","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"The main breaking changes in ITensor.jl v0.2 involve changes to the ITensor, IndexSet, and IndexVal types. Most user code should be fine, but see below for more details.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"In addition, we have moved development of NDTensors.jl into ITensors.jl to simplify the development process until NDTensors is more stable and can be a standalone package. Again, see below for more details.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"For a more comprehensive list of changes, see the commit history on Github.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"If you have issues upgrading, please reach out by raising an issue on Github or asking a question on the ITensor support forum.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Also make sure to run your code with julia --depwarn=yes to see warnings about function names and interfaces that have been deprecated and will be removed in v0.3 of ITensors.jl (these are not listed here).","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#Major-design-changes:-changes-to-the-ITensor,-IndexSet,-and-IndexVal-types","page":"Upgrading from 0.1 to 0.2","title":"Major design changes: changes to the ITensor, IndexSet, and IndexVal types","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html#Changes-to-the-ITensor-type","page":"Upgrading from 0.1 to 0.2","title":"Changes to the ITensor type","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html#Removal-of-tensor-order-type-parameter","page":"Upgrading from 0.1 to 0.2","title":"Removal of tensor order type parameter","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"The tensor order type paramater has been removed from the ITensor type, so you can no longer write ITensor{3} to specify an order 3 ITensor (PR #591). Code that uses the ITensor order type parameter will now lead to the following error:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> i = Index(2)\n(dim=2|id=588)\n\njulia> ITensor{2}(i', i)\nERROR: TypeError: in Type{...} expression, expected UnionAll, got Type{ITensor}\nStacktrace:\n [1] top-level scope\n @ REPL[27]:1","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Simply remove the type parameter:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> ITensor(i', i)\nITensor ord=2 (dim=2|id=913)' (dim=2|id=913)\nITensors.NDTensors.EmptyStorage{ITensors.NDTensors.EmptyNumber, ITensors.NDTensors.Dense{ITensors.NDTensors.EmptyNumber, Vector{ITensors.NDTensors.EmptyNumber}}}","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Pro tip: from the command line, you can replace all examples like that with:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"find . -type f -iname \"*.jl\" -exec sed -i 's/ITensor{.*}/ITensor/g' \"{}\" +","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Of course, make sure to back up your code before running this!","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Additionally, a common code pattern may be using the type parameter for dispatch:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"using ITensors\n\nfunction mynorm(A::ITensor{N}) where {N}\n return norm(A)^N\nend\n\nfunction mynorm(A::ITensor{1})\n return norm(A)\nend\n\nfunction mynorm(A::ITensor{2})\n return norm(A)^2\nend","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Instead, you can use an if-statement:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"function mynormN(A::ITensor)\n return norm(A)^order(A)\nend\n\nfunction mynorm1(A::ITensor)\n return norm(A)\nend\n\nfunction mynorm2(A::ITensor)\n return norm(A)^2\nend\n\nfunction mynorm(A::ITensor)\n return if order(A) == 1\n mynorm1(A)\n elseif order(A) == 2\n mynorm2(A)\n else\n return mynormN(A)\n end\nend","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Alternatively, you can use the Order type to dispatch on the ITensor order as follows:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"function mynorm(::Order{N}, A::ITensor) where {N}\n return norm(A)^N\nend\n\nfunction mynorm(::Order{1}, A::ITensor)\n return norm(A)\nend\n\nfunction mynorm(::Order{2}, A::ITensor)\n return norm(A)^2\nend\n\nfunction mynorm(A::ITensor)\n return mynorm(Order(A), A)\nend","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Order(A::ITensor) returns the order of the ITensor (like order(A::ITensor)), however as a type that can be dispatched on. Note that it is not type stable, so there will be a small runtime overhead for doing this.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#Change-to-storage-type-of-Index-collection-in-ITensor","page":"Upgrading from 0.1 to 0.2","title":"Change to storage type of Index collection in ITensor","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"ITensors now store a Tuple of Index instead of an IndexSet (PR #626). Therefore, calling inds on an ITensor will now just return a Tuple:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> i = Index(2)\n(dim=2|id=770)\n\njulia> j = Index(3)\n(dim=3|id=272)\n\njulia> A = randomITensor(i, j)\nITensor ord=2 (dim=2|id=770) (dim=3|id=272)\nITensors.NDTensors.Dense{Float64, Vector{Float64}}\n\njulia> inds(A)\n((dim=2|id=770), (dim=3|id=272))","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"while before it returned an IndexSet (in fact, the IndexSet type has been removed, see below for details). In general, this should not affect user code, since a Tuple of Index should have all of the same functions defined for it that IndexSet did. If you find this is not the case, please raise an issue on Github or on the ITensor support forum.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#ITensor-type-now-directly-wraps-a-Tensor","page":"Upgrading from 0.1 to 0.2","title":"ITensor type now directly wraps a Tensor","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"The ITensor type no longer has separate field inds and store, just a single field tensor (PR #626). In general you should not be accessing the fields directly, instead you should be using the functions inds(A::ITensor) and storage(A::ITensor), so this should not affect most code. However, in case you have code like:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"i = Index(2)\nA = randomITensor(i)\nA.inds","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"this will error in v0.2 with:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> A.inds\nERROR: type ITensor has no field inds\nStacktrace:\n [1] getproperty(x::ITensor, f::Symbol)\n @ Base ./Base.jl:33\n [2] top-level scope\n @ REPL[43]:1","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"and you should change it to:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"inds(A)","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#Changes-to-the-ITensor-constructors","page":"Upgrading from 0.1 to 0.2","title":"Changes to the ITensor constructors","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html#Plain-ITensor-constructors-now-return-ITensors-with-EmptyStorage-storage","page":"Upgrading from 0.1 to 0.2","title":"Plain ITensor constructors now return ITensors with EmptyStorage storage","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"ITensor constructors from collections of Index, such as ITensor(i, j, k), now return an ITensor with EmptyStorage (previously called Empty) storage instead of Dense or BlockSparse storage filled with 0 values. Most operations should still work that worked previously, but please contact us if there are issues (PR #641).","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"For example:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> i = Index(2)\n(dim=2|id=346)\n\njulia> A = ITensor(i', dag(i))\nITensor ord=2 (dim=2|id=346)' (dim=2|id=346)\nITensors.NDTensors.EmptyStorage{ITensors.NDTensors.EmptyNumber, ITensors.NDTensors.Dense{ITensors.NDTensors.EmptyNumber, Vector{ITensors.NDTensors.EmptyNumber}}}\n\njulia> A' * A\nITensor ord=2 (dim=2|id=346)'' (dim=2|id=346)\nITensors.NDTensors.EmptyStorage{ITensors.NDTensors.EmptyNumber, ITensors.NDTensors.Dense{ITensors.NDTensors.EmptyNumber, Vector{ITensors.NDTensors.EmptyNumber}}}","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"so now contracting two EmptyStorage ITensors returns another EmptyStorage ITensor. You can allocate the storage by setting elements of the ITensor:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> A[i' => 1, i => 1] = 0.0\n0.0\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=346)'\nDim 2: (dim=2|id=346)\nITensors.NDTensors.Dense{Float64, Vector{Float64}}\n 2×2\n 0.0 0.0\n 0.0 0.0","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Additionally, it will take on the element type of the first value set:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> A = ITensor(i', dag(i))\nITensor ord=2 (dim=2|id=346)' (dim=2|id=346)\nITensors.NDTensors.EmptyStorage{ITensors.NDTensors.EmptyNumber, ITensors.NDTensors.Dense{ITensors.NDTensors.EmptyNumber, Vector{ITensors.NDTensors.EmptyNumber}}}\n\njulia> A[i' => 1, i => 1] = 1.0 + 0.0im\n1.0 + 0.0im\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=346)'\nDim 2: (dim=2|id=346)\nITensors.NDTensors.Dense{ComplexF64, Vector{ComplexF64}}\n 2×2\n 1.0 + 0.0im 0.0 + 0.0im\n 0.0 + 0.0im 0.0 + 0.0im","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"If you have issues upgrading, please let us know.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#Slight-change-to-automatic-conversion-of-element-type-when-constructing-ITensor-from-Array","page":"Upgrading from 0.1 to 0.2","title":"Slight change to automatic conversion of element type when constructing ITensor from Array","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"ITensor constructors from Array now only convert to floating point for Array{Int} and Array{Complex{Int}}. That same conversion is added for QN ITensor constructors to be consistent with non-QN versions (PR #620). Previously it tried to convert arrays of any element type to the closest floating point type with Julia's float function. This should not affect most user code.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#Changes-to-the-IndexSet-type","page":"Upgrading from 0.1 to 0.2","title":"Changes to the IndexSet type","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"The IndexSet type has been removed in favor of Julia's Tuple and Vector types (PR #626). ITensors now contain a Tuple of Index, while set operations like commoninds that used to return IndexSet now return a Vector of Index:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> i = Index(2)\n(dim=2|id=320)\n\njulia> A = randomITensor(i', i)\nITensor ord=2 (dim=2|id=320)' (dim=2|id=320)\nITensors.NDTensors.Dense{Float64, Vector{Float64}}\n\njulia> inds(A) # Previously returned IndexSet, now returns Tuple\n((dim=2|id=320)', (dim=2|id=320))\n\njulia> commoninds(A', A) # Previously returned IndexSet, now returns Vector\n1-element Vector{Index{Int64}}:\n (dim=2|id=320)'","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"To help with upgrading code, IndexSet{IndexT} has been redefined as a type alias for Vector{IndexT<:Index} (which is subject to change to some other collection of indices, and likely will be removed in ITensors v0.3). Therefore it no longer has a type parameter for the number of indices, similar to the change to the ITensor type. If you were using the plain IndexSet type, code should generally still work properly. However, if you were using the type parameters of IndexSet, such as:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"function myorder2(is::IndexSet{N}) where {N}\n return N^2\nend","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"then you will need to remove the type parameter and rewrite your code generically to accept Tuple or Vector, such as:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"function myorder2(is)\n return length(is)^2\nend","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"In general you should be able to just remove usages of IndexSet in your code, and can just use Tuple or Vector of Index instead, such as change is = IndexSet(i, j, k) to is = (i, j, k) or is = [i, j, k]. Priming, tagging, and set operations now work generically on those types. If you see issues with upgrading your code, please let us know.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#Changes-to-the-IndexVal-type","page":"Upgrading from 0.1 to 0.2","title":"Changes to the IndexVal type","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Similar to the removal of IndexSet, we have also removed the IndexVal type (PR #665). Now, all use cases of IndexVal can be replaced by using Julia's Pair type, for example instead of:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"i = Index(2)\nIndexVal(i, 2)","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"use:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"i = Index(2)\ni => 2\n# Or:\nPair(i, 2)","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Note that we have made IndexVal{IndexT} an alias for Pair{IndexT,Int}, so code using IndexVal such as IndexVal(i, 2) should generally still work. However, we encourage users to change from IndexVal(i, 2) to i => 2.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#NDTensors.jl-package-now-being-developed-internally-within-ITensors.jl","page":"Upgrading from 0.1 to 0.2","title":"NDTensors.jl package now being developed internally within ITensors.jl","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"The NDTensors module has been moved into the ITensors package, so ITensors no longer depends on the standalone NDTensors package. This should only effect users who were using both NDTensors and ITensors seperately. If you want to use the latest NDTensors library, you should do using ITensors.NDTensors instead of using NDTensors, and will need to install ITensors with using Pkg; Pkg.add(\"ITensors\") in order to use the latest versions of NDTensors. Note the current NDTensors.jl package will still exist, but for now developmentof NDTensors will occur within ITensors.jl (PR #650).","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#Miscellaneous-breaking-changes","page":"Upgrading from 0.1 to 0.2","title":"Miscellaneous breaking changes","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html#state-function-renamed-val,-state-given-a-new-more-general-definition","page":"Upgrading from 0.1 to 0.2","title":"state function renamed val, state given a new more general definition","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Rename the state functions currently defined for various site types to val for mapping a string name for an index to an index value (used in ITensor indexing and MPS construction). state functions now return single-index ITensors representing various single-site states (PR #664). So now to get an Index value from a string, you use:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"N = 10\ns = siteinds(\"S=1/2\", N)\nval(s[1], \"Up\") == 1\nval(s[1], \"Dn\") == 2","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"state now returns an ITensor corresponding to the state with that value as the only nonzero element:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> @show state(s[1], \"Up\");\nstate(s[1], \"Up\") = ITensor ord=1\nDim 1: (dim=2|id=597|\"S=1/2,Site,n=1\")\nITensors.NDTensors.Dense{Float64, Vector{Float64}}\n 2-element\n 1.0\n 0.0\n\njulia> @show state(s[1], \"Dn\");\nstate(s[1], \"Dn\") = ITensor ord=1\nDim 1: (dim=2|id=597|\"S=1/2,Site,n=1\")\nITensors.NDTensors.Dense{Float64, Vector{Float64}}\n 2-element\n 0.0\n 1.0","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"which allows for more general states to be defined, such as:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> @show state(s[1], \"X+\");\nstate(s[1], \"X+\") = ITensor ord=1\nDim 1: (dim=2|id=597|\"S=1/2,Site,n=1\")\nITensors.NDTensors.Dense{Float64, Vector{Float64}}\n 2-element\n 0.7071067811865475\n 0.7071067811865475\n\njulia> @show state(s[1], \"X-\");\nstate(s[1], \"X-\") = ITensor ord=1\nDim 1: (dim=2|id=597|\"S=1/2,Site,n=1\")\nITensors.NDTensors.Dense{Float64, Vector{Float64}}\n 2-element\n 0.7071067811865475\n -0.7071067811865475","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"which will be used for making more general MPS product states.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"This should not affect end users in general, besides ones who had customized the previous state function, such as with overloads like:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"ITensors.state(::SiteType\"My_S=1/2\", ::StateName\"Up\") = 1\nITensors.state(::SiteType\"My_S=1/2\", ::StateName\"Dn\") = 2","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"which should be changed now to:","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"ITensors.val(::SiteType\"My_S=1/2\", ::StateName\"Up\") = 1\nITensors.val(::SiteType\"My_S=1/2\", ::StateName\"Dn\") = 2","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#\"Qubit\"-site-type-QN-convention-change","page":"Upgrading from 0.1 to 0.2","title":"\"Qubit\" site type QN convention change","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"The QN convention of the \"Qubit\" site type is changed to track the total number of 1 bits instead of the net number of 1 bits vs 0 bits (i.e. change the QN from +1/-1 to 0/1) (PR #676).","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> s = siteinds(\"Qubit\", 4; conserve_number=true)\n4-element Vector{Index{Vector{Pair{QN, Int64}}}}:\n (dim=2|id=925|\"Qubit,Site,n=1\") \n 1: QN(\"Number\",0) => 1\n 2: QN(\"Number\",1) => 1\n (dim=2|id=799|\"Qubit,Site,n=2\") \n 1: QN(\"Number\",0) => 1\n 2: QN(\"Number\",1) => 1\n (dim=2|id=8|\"Qubit,Site,n=3\") \n 1: QN(\"Number\",0) => 1\n 2: QN(\"Number\",1) => 1\n (dim=2|id=385|\"Qubit,Site,n=4\") \n 1: QN(\"Number\",0) => 1\n 2: QN(\"Number\",1) => 1","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"Before it was +1/-1 like \"S=1/2\":","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"julia> s = siteinds(\"S=1/2\", 4; conserve_sz=true)\n4-element Vector{Index{Vector{Pair{QN, Int64}}}}:\n (dim=2|id=364|\"S=1/2,Site,n=1\") \n 1: QN(\"Sz\",1) => 1\n 2: QN(\"Sz\",-1) => 1\n (dim=2|id=823|\"S=1/2,Site,n=2\") \n 1: QN(\"Sz\",1) => 1\n 2: QN(\"Sz\",-1) => 1\n (dim=2|id=295|\"S=1/2,Site,n=3\") \n 1: QN(\"Sz\",1) => 1\n 2: QN(\"Sz\",-1) => 1\n (dim=2|id=810|\"S=1/2,Site,n=4\") \n 1: QN(\"Sz\",1) => 1\n 2: QN(\"Sz\",-1) => 1","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"This shouldn't affect end users in general. The new convention is a bit more intuitive since the quantum number can be thought of as counting the total number of 1 bits in the state, though the conventions can be mapped to each other with a constant.","category":"page"},{"location":"UpgradeGuide_0.1_to_0.2.html#maxlinkdim-for-MPS/MPO-with-no-indices","page":"Upgrading from 0.1 to 0.2","title":"maxlinkdim for MPS/MPO with no indices","text":"","category":"section"},{"location":"UpgradeGuide_0.1_to_0.2.html","page":"Upgrading from 0.1 to 0.2","title":"Upgrading from 0.1 to 0.2","text":"maxlinkdim(::MPS/MPO) returns a minimum of 1 (previously it returned 0 for MPS/MPO without and link indices) (PR #663).","category":"page"},{"location":"index.html#ITensors.jl","page":"Introduction","title":"ITensors.jl","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"ITensor is a library for rapidly creating correct and efficient tensor network algorithms.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Documentation Citation Build Status\n(Image: docs) (Image: SciPost) (Image: arXiv) (Image: Tests) (Image: codecov)","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Version Download Statistics Style Guide License\n(Image: version) (Image: ITensor Downloads) (Image: Code Style: Blue) (Image: license)","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"The source code for ITensor can be found on Github.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Additional documentation can be found on the ITensor website itensor.org.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"An ITensor is a tensor whose interface is independent of its memory layout. ITensor indices are objects which carry extra information and which 'recognize' each other (compare equal to each other).","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"The ITensor library also includes composable and extensible algorithms for optimizing and transforming tensor networks, such as matrix product state and matrix product operators, such as the DMRG algorithm.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Development of ITensor is supported by the Flatiron Institute, a division of the Simons Foundation.","category":"page"},{"location":"index.html#News","page":"Introduction","title":"News","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"May 2, 2024: ITensors.jl v0.5 has been released. This version removes PackageCompiler.jl as a dependency and moves the package compilation functionality into a package extension. In order to use the ITensors.compile() function going forward, you need to install the PackageCompiler.jl package with using Pkg: Pkg; Pkg.add(\"PackageCompiler\") and put using PackageCompiler together with using ITensors in your code.\nApril 16, 2024: ITensors.jl v0.4 has been released. This version removes HDF5.jl as a dependency and moves the HDF5 read and write functions for ITensor, MPS, MPO, and other associated types into a package extension. To enable ITensor HDF5 features, install the HDF5.jl package with using Pkg: Pkg; Pkg.add(\"HDF5\") and put using HDF5 together with using ITensors in your code. Other recent changes include support for multiple GPU backends using package extensions.\nMarch 25, 2022: ITensors.jl v0.3 has been released. The main breaking change is that we no longer support versions of Julia below 1.6. Julia 1.6 is the long term support version of Julia (LTS), which means that going forward versions below Julia 1.6 won't be as well supported with bug fixes and improvements. Additionally, Julia 1.6 introduced many improvements including syntax improvements that we would like to start using with ITensors.jl, which becomes challenging if we try to support Julia versions below 1.6. See here and here for some nice summaries of the Julia 1.6 release.\nJun 09, 2021: ITensors.jl v0.2 has been released, with a few breaking changes as well as a variety of bug fixes","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"and new features. Take a look at the upgrade guide for help upgrading your code.","category":"page"},{"location":"index.html#Installation","page":"Introduction","title":"Installation","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"The ITensors package can be installed with the Julia package manager. From the Julia REPL, type ] to enter the Pkg REPL mode and run:","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"~ julia","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"julia> ]\n\npkg> add ITensors","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Or, equivalently, via the Pkg API:","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"julia> import Pkg; Pkg.add(\"ITensors\")","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Please note that right now, ITensors.jl requires that you use Julia v1.3 or later (since ITensors.jl relies on a feature that was introduced in Julia v1.3).","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"We recommend using ITensors.jl with Intel MKL in order to get the best possible performance. If you have not done so already, you can replace your current BLAS and LAPACK implementation with MKL by using the MKL.jl package. Please follow the instructions here.","category":"page"},{"location":"index.html#Documentation","page":"Introduction","title":"Documentation","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"LATEST – documentation of the latest version.","category":"page"},{"location":"index.html#Citation","page":"Introduction","title":"Citation","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"If you use ITensor in your work, please cite the ITensor Paper:","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"@article{ITensor,\n\ttitle={{The ITensor Software Library for Tensor Network Calculations}},\n\tauthor={Matthew Fishman and Steven R. White and E. Miles Stoudenmire},\n\tjournal={SciPost Phys. Codebases},\n\tpages={4},\n\tyear={2022},\n\tpublisher={SciPost},\n\tdoi={10.21468/SciPostPhysCodeb.4},\n\turl={https://scipost.org/10.21468/SciPostPhysCodeb.4},\n}","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"and associated \"Codebase Release\" for the version you have used. The current one is","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"@article{ITensor-r0.3,\n\ttitle={{Codebase release 0.3 for ITensor}},\n\tauthor={Matthew Fishman and Steven R. White and E. Miles Stoudenmire},\n\tjournal={SciPost Phys. Codebases},\n\tpages={4-r0.3},\n\tyear={2022},\n\tpublisher={SciPost},\n\tdoi={10.21468/SciPostPhysCodeb.4-r0.3},\n\turl={https://scipost.org/10.21468/SciPostPhysCodeb.4-r0.3},\n}","category":"page"},{"location":"index.html#ITensor-Code-Samples","page":"Introduction","title":"ITensor Code Samples","text":"","category":"section"},{"location":"index.html#Basic-Overview","page":"Introduction","title":"Basic Overview","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"ITensor construction, setting of elements, contraction, and addition. Before constructing an ITensor, one constructs Index objects representing tensor indices.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"using ITensors\nlet\n i = Index(3)\n j = Index(5)\n k = Index(2)\n l = Index(7)\n\n A = ITensor(i,j,k)\n B = ITensor(j,l)\n\n # Set elements of A\n A[i=>1,j=>1,k=>1] = 11.1\n A[i=>2,j=>1,k=>2] = -21.2\n A[k=>1,i=>3,j=>1] = 31.1 # can provide Index values in any order\n # ...\n\n # Contract over shared index j\n C = A * B\n\n @show hasinds(C,i,k,l) # = true\n\n D = randomITensor(k,j,i) # ITensor with random elements\n\n # Add two ITensors\n # must have same set of indices\n # but can be in any order\n R = A + D\n\n nothing\nend\n\n# output\n\nhasinds(C, i, k, l) = true","category":"page"},{"location":"index.html#Singular-Value-Decomposition-(SVD)-of-a-Matrix","page":"Introduction","title":"Singular Value Decomposition (SVD) of a Matrix","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"In this example, we create a random 10x20 matrix and compute its SVD. The resulting factors can be simply multiplied back together using the ITensor * operation, which automatically recognizes the matching indices between U and S, and between S and V and contracts (sums over) them.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"using ITensors\nlet\n i = Index(10) # index of dimension 10\n j = Index(20) # index of dimension 20\n M = randomITensor(i,j) # random matrix, indices i,j\n U,S,V = svd(M,i) # compute SVD with i as row index\n @show M ≈ U*S*V # = true\n\n nothing\nend\n\n# output\n\nM ≈ U * S * V = true","category":"page"},{"location":"index.html#Singular-Value-Decomposition-(SVD)-of-a-Tensor","page":"Introduction","title":"Singular Value Decomposition (SVD) of a Tensor","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"In this example, we create a random 4x4x4x4 tensor and compute its SVD, temporarily treating the indices i and k together as the \"row\" index and j and l as the \"column\" index for the purposes of the SVD. The resulting factors can be simply multiplied back together using the ITensor * operation, which automatically recognizes the matching indices between U and S, and between S and V and contracts (sums over) them.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"(Image: )","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"using ITensors\nlet\n i = Index(4,\"i\")\n j = Index(4,\"j\")\n k = Index(4,\"k\")\n l = Index(4,\"l\")\n T = randomITensor(i,j,k,l)\n U,S,V = svd(T,i,k) # compute SVD with (i,k) as row indices (indices of U)\n @show hasinds(U,i,k) # = true\n @show hasinds(V,j,l) # = true\n @show T ≈ U*S*V # = true\n\n nothing\nend\n\n# output\n\nhasinds(U, i, k) = true\nhasinds(V, j, l) = true\nT ≈ U * S * V = true","category":"page"},{"location":"index.html#Tensor-Indices:-Tags-and-Prime-Levels","page":"Introduction","title":"Tensor Indices: Tags and Prime Levels","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Before making an ITensor, you have to define its indices. Tensor Index objects carry extra information beyond just their dimension.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"All Index objects carry a permanent, immutable id number which is determined when it is constructed, and allow it to be matched (compare equal) with copies of itself.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Additionally, an Index can have up to four tag strings, and an integer primelevel. If two Index objects have different tags or different prime levels, they do not compare equal even if they have the same id.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"Tags are also useful for identifying Index objects when printing tensors, and for performing certain Index manipulations (e.g. priming indices having certain sets of tags).","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"using ITensors\nlet\n i = Index(3) # Index of dimension 3\n @show dim(i) # = 3\n @show id(i) # = 0x5d28aa559dd13001 or similar\n\n ci = copy(i)\n @show ci == i # = true\n\n j = Index(5,\"j\") # Index with a tag \"j\"\n\n @show j == i # = false\n\n s = Index(2,\"n=1,Site\") # Index with two tags,\n # \"Site\" and \"n=1\"\n @show hastags(s,\"Site\") # = true\n @show hastags(s,\"n=1\") # = true\n\n i1 = prime(i) # i1 has a \"prime level\" of 1\n # but otherwise same properties as i\n @show i1 == i # = false, prime levels do not match\n\n nothing\nend\n\n# output\n\ndim(i) = 3\nid(i) = 0x5d28aa559dd13001\nci == i = true\nj == i = false\nhastags(s, \"Site\") = true\nhastags(s, \"n=1\") = true\ni1 == i = false","category":"page"},{"location":"index.html#DMRG-Calculation","page":"Introduction","title":"DMRG Calculation","text":"","category":"section"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"DMRG is an iterative algorithm for finding the dominant eigenvector of an exponentially large, Hermitian matrix. It originates in physics with the purpose of finding eigenvectors of Hamiltonian (energy) matrices which model the behavior of quantum systems.","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"using ITensors\nlet\n # Create 100 spin-one indices\n N = 100\n sites = siteinds(\"S=1\",N)\n\n # Input operator terms which define\n # a Hamiltonian matrix, and convert\n # these terms to an MPO tensor network\n # (here we make the 1D Heisenberg model)\n os = OpSum()\n for j=1:N-1\n os += \"Sz\",j,\"Sz\",j+1\n os += 0.5,\"S+\",j,\"S-\",j+1\n os += 0.5,\"S-\",j,\"S+\",j+1\n end\n H = MPO(os,sites)\n\n # Create an initial random matrix product state\n psi0 = randomMPS(sites)\n\n # Plan to do 5 passes or 'sweeps' of DMRG,\n # setting maximum MPS internal dimensions\n # for each sweep and maximum truncation cutoff\n # used when adapting internal dimensions:\n nsweeps = 5\n maxdim = [10,20,100,100,200]\n cutoff = 1E-10\n\n # Run the DMRG algorithm, returning energy\n # (dominant eigenvalue) and optimized MPS\n energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff)\n println(\"Final energy = $energy\")\n\n nothing\nend\n\n# output\n\nAfter sweep 1 energy=-137.954199761732 maxlinkdim=9 maxerr=2.43E-16 time=9.356\nAfter sweep 2 energy=-138.935058943878 maxlinkdim=20 maxerr=4.97E-06 time=0.671\nAfter sweep 3 energy=-138.940080155429 maxlinkdim=92 maxerr=1.00E-10 time=4.522\nAfter sweep 4 energy=-138.940086009318 maxlinkdim=100 maxerr=1.05E-10 time=11.644\nAfter sweep 5 energy=-138.940086058840 maxlinkdim=96 maxerr=1.00E-10 time=12.771\nFinal energy = -138.94008605883985","category":"page"},{"location":"index.html","page":"Introduction","title":"Introduction","text":"You can find more examples of running dmrg and related algorithms here.","category":"page"},{"location":"tutorials/QN_DMRG.html#Quantum-Number-Conserving-DMRG","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"","category":"section"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"An important technique in DMRG calculations of quantum Hamiltonians is the conservation of quantum numbers. Examples of these are the total number of particles of a model of fermions, or the total of all S^z components of a system of spins. Not only can conserving quantum numbers make DMRG calculations run more quickly and use less memory, but it can be important for simulating physical systems with conservation laws and for obtaining ground states in different symmetry sectors. Note that ITensor currently only supports Abelian quantum numbers.","category":"page"},{"location":"tutorials/QN_DMRG.html#Necessary-Changes","page":"Quantum Number Conserving DMRG","title":"Necessary Changes","text":"","category":"section"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Setting up a quantum-number conserving DMRG calculation in ITensor requires only very small changes to a DMRG code. The main changes are:","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"using tensor indices (Index objects) which carry quantum number (QN) information to build your Hamiltonian and initial state\ninitializing your MPS to have well-defined total quantum numbers","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Importantly, the total QN of your state throughout the calculation will remain the same as the initial state passed to DMRG. The total QN of your state is not set separately, but determined implicitly from the initial QN of the state when it is first constructed.","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Of course, your Hamiltonian should conserve all of the QN's that you would like to use. If it doesn't, you will get an error when you try to construct it out of the QN-enabled tensor indices.","category":"page"},{"location":"tutorials/QN_DMRG.html#Making-the-Changes","page":"Quantum Number Conserving DMRG","title":"Making the Changes","text":"","category":"section"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Let's see how to make these two changes to the DMRG Tutorial code from the previous section. At the end, we will put together these changes for a complete, working code.","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Change 1: QN Site Indices","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"To make change (1), we will change the line","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"sites = siteinds(\"S=1\",N)","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"by setting the conserve_qns keyword argument to true:","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"sites = siteinds(\"S=1\",N; conserve_qns=true)","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Setting conserve_qns=true tells the siteinds function to conserve every possible quantum number associated to the site type (which is \"S=1\" in this example). For S=1 spins, this will turn on total-S^z conservation. (For other site types that conserve multiple QNs, there are specific keyword arguments available to track just a subset of conservable QNs.) We can check this by printing out some of the site indices, and seeing that the subspaces of each Index are labeled by QN values:","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"@show sites[1]\n@show sites[2]","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Sample output:","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":" sites[1] = (dim=3|id=794|\"S=1,Site,n=1\") \n 1: QN(\"Sz\",2) => 1\n 2: QN(\"Sz\",0) => 1\n 3: QN(\"Sz\",-2) => 1\n sites[2] = (dim=3|id=806|\"S=1,Site,n=2\") \n 1: QN(\"Sz\",2) => 1\n 2: QN(\"Sz\",0) => 1\n 3: QN(\"Sz\",-2) => 1","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"In the sample output above, note that in addition to the dimension of these indices being 3, each of the three settings of the Index have a unique QN associated to them. The number after the QN on each line is the dimension of that subspace, which is 1 for each subspace of the Index objects above. Note also that \"Sz\" quantum numbers in ITensor are measured in units of 12, so QN(\"Sz\",2) corresponds to S^z=1 in conventional physics units.","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Change 2: Initial State","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"To make change (2), instead of constructing the initial MPS psi0 to be an arbitrary, random MPS, we will make it a specific state with a well-defined total S^z. So we will replace the line","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"psi0 = randomMPS(sites,10)","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"by the lines","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"state = [isodd(n) ? \"Up\" : \"Dn\" for n=1:N]\npsi0 = MPS(sites,state)","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"The first line of the new code above makes an array of strings which alternate between \"Up\" and \"Dn\" on odd and even numbered sites. These names \"Up\" and \"Dn\" are special values associated to the \"S=1\" site type which indicate up and down spin values. The second line takes the array of site Index objects sites and the array of strings state and returns an MPS which is a product state (classical, unentangled state) with each site's state given by the strings in the state array. In this example, psi0 will be a Neel state with alternating up and down spins, so it will have a total S^z of zero. We could check this by computing the quantum-number flux of psi0","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"@show flux(psi0)\n# Output: flux(psi0) = QN(\"Sz\",0)","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"info: Setting Other Total QN Values\nThe above example shows the case of setting a total \"Sz\" quantum number of zero, since the initial state alternates between \"Up\" and \"Dn\" on every site with an even number of sites.To obtain other total QN values, just set the initial state to be one which has the total QN you want. To be concrete let's take the example of a system with N=10 sites of S=1 spins.For example if you want a total \"Sz\" of +20 (= QN(\"Sz\",20)) in ITensor units, or S^z=10 in physical units, for a system with 10 sites, use the initial state:state = [\"Up\" for n=1:N]\npsi0 = MPS(sites,state)Or to initialize this 10-site system to have a total \"Sz\" of +16 in ITensor units (S^z=8 in physical units):state = [\"Dn\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\"]\npsi0 = MPS(sites,state)would work (as would any state with one \"Dn\" and nine \"Up\"'s in any order). Or you could initialize to a total \"Sz\" of +18 in ITensor units (S^z=9 in physical units) asstate = [\"Z0\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\",\"Up\"]\npsi0 = MPS(sites,state)where \"Z0\" refers to the S^z=0 state of a spin-one spin.Finally, the same kind of logic as above applies to other physical site types, whether \"S=1/2\", \"Electron\", etc.","category":"page"},{"location":"tutorials/QN_DMRG.html#Putting-it-All-Together","page":"Quantum Number Conserving DMRG","title":"Putting it All Together","text":"","category":"section"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"Let's take the DMRG Tutorial code from the previous section and make the changes discussed above, to turn it into a code which conserves the total S^z quantum number throughout the DMRG calculation. The resulting code is:","category":"page"},{"location":"tutorials/QN_DMRG.html","page":"Quantum Number Conserving DMRG","title":"Quantum Number Conserving DMRG","text":"using ITensors\nlet\n N = 100\n sites = siteinds(\"S=1\",N;conserve_qns=true)\n\n os = OpSum()\n for j=1:N-1\n os += \"Sz\",j,\"Sz\",j+1\n os += 1/2,\"S+\",j,\"S-\",j+1\n os += 1/2,\"S-\",j,\"S+\",j+1\n end\n H = MPO(os,sites)\n\n state = [isodd(n) ? \"Up\" : \"Dn\" for n=1:N]\n psi0 = MPS(sites,state)\n @show flux(psi0)\n\n nsweeps = 5\n maxdim = [10,20,100,100,200]\n cutoff = [1E-10]\n\n energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff)\n\n return\nend","category":"page"},{"location":"faq/DMRG.html#Density-Matrix-Renormalization-Group-(DMRG)-Frequently-Asked-Questions","page":"DMRG FAQs","title":"Density Matrix Renormalization Group (DMRG) Frequently Asked Questions","text":"","category":"section"},{"location":"faq/DMRG.html#Ensuring-a-DMRG-calculation-is-converged","page":"DMRG FAQs","title":"Ensuring a DMRG calculation is converged","text":"","category":"section"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"While DMRG calculations can be extremely quick to converge in the best cases, convergence can be slower for cases such as gapless systems or quasi-two-dimensional systems. So it becomes important to know if a DMRG calculation is converged i.e. has been run long enough with enough resources (large enough MPS bond dimension).","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Unfortunately there is no automatic or bulletproof check for DMRG convergence. However, there are a number of reliable heuristics you can use to check convergence. We list some of these with the most fundamental and important ones first:","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Run your DMRG calculation on a smaller system and compare with another method, such as an exact diagonalization. If the agreement is good, then gradually try larger systems and see if the physical properties are roughly consistent and similar (i.e. the density profile has similar features).\nMake sure to check a wide range of properties - not just the energy. See if these look plausible by plotting and visually inspecting them. For example: if your system has left-right reflection symmetry, does the density or magnetization also have this symmetry? If the ground state of your system is expected to have a total S^z of zero, does your ground state have this property?\nMake sure to run your DMRG calculation for different numbers of sweeps to see if the results change. For example, if you run DMRG for 5 sweeps but are unsure of convergence, try running it for 10 sweeps: is the energy the same or has it significantly decreased? If 10 sweeps made a difference, try 20 sweeps.\nTry setting the eigsolve_krylovdim keyword argument to a higher value (the default is 3). This can be particularily helpful when the Hamiltonian is close to a critical point. This may make slowly-converging calculations converge in fewer sweeps, but setting it too high can make each sweep run slowly.\nInspect the the DMRG output. The ITensor DMRG code reports the maximum bond or link dimension and maximum truncation error after each sweep. (The maximums here mean over each DMRG substep making up one sweep.) Is the maximum dimension or \"maxlinkdim\" reported by the DMRG output quickly reaching and saturating the maxdim value you set for each sweep? Is the maximum truncation error \"maxerr\" consistently reaching large values, larger than 1E-5? Then it you may need to raise the maxdim parameter for your later sweeps, so that DMRG is allowed to use a larger bond dimension and thus reach a better accuracy.\nCompute the energy variance of an MPS to check whether it is an eigenstate. To do this in ITensor, you can use the following code where H is your Hamiltonian MPO and psi is the wavefunction you want to check:\nH2 = inner(H,psi,H,psi)\nE = inner(psi',H,psi)\nvar = H2-E^2\n@show var\nHere var is the quantity langle H^2 rangle - langle H rangle^2. The closer var is to zero, the more precisely psi is an eigenstate of H. Note that this check does not ensure that psi is the ground state, but only one of the eigenstates.","category":"page"},{"location":"faq/DMRG.html#Preventing-DMRG-from-getting-stuck-in-a-local-minimum","page":"DMRG FAQs","title":"Preventing DMRG from getting stuck in a local minimum","text":"","category":"section"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"While DMRG has very robust convergence properties when the initial MPS is close to the global minimum, if it is far from the global minumum then there is no guarantee that DMRG will be able to find the true ground state. This problem is exacerbated for quantum number conserving DMRG where the search space is more constrained.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Thus it is very important to perform a number of checks to ensure that the result you get from DMRG is actually converged. To learn about these checks, see the previous question.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"When DMRG is failing to converge, here are some of the steps you can take to improve things:","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"The most important and useful technique is to turn on the noise term feature of DMRG. To do this, just set the noise parameter of each sweep to a small, non-zero value, making this value very small (1E-11, say) or zero by the last sweep. (Experiment with different values on small systems to see which noise magnitudes help.) Here is an example of defining DMRG accuracy or sweep parameters with a non-zero noise set for the first three sweeps:\nnsweeps = 10\nmaxdim = [100, 200, 400, 800, 1600]\ncutoff = [1E-6]\nnoise = [1E-6, 1E-7, 1E-8, 0.0]\n...\nenergy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff, noise)\nTry using a initial MPS with properties close to the ground state you are looking for. For example, the ground state of a system of electrons typically has a density which is spread out over the whole system. So if your initial state has all of the electrons bunched up on the left-hand side only, it can take DMRG a very long time to converge.\nTry using a random MPS with a modestly large bond dimension. ITensor offers a function called randomMPS which can be used to make random MPS in both the quantum number (QN) conserving and non-QN conserving cases. Because random MPS have properties which are \"typical\" of most ground states, they can be good initial states for DMRG.\nTry DMRG on a closely related Hamiltonian for which convergence is easier to obtain (be creative here: it could be your Hamiltonian with interactions turned off, or with interactions only within, but not between, small local patches). Take the output of this first calculation and use it as input for DMRG with the full Hamiltonian.\nIn stubborn cases, try other methods for finding the ground state which are slower, but have a better chance of succeeding. A key example is imaginary time evolution, which always reaches the ground state if (a) performed accurately on (b) a state which is not orthogonal to the ground state. After doing some amount of imaginary time evolution, use the resulting MPS as an initial state for DMRG obtain a higher-accuracy solution.","category":"page"},{"location":"faq/DMRG.html#How-to-do-periodic-boundary-condition-DMRG","page":"DMRG FAQs","title":"How to do periodic boundary condition DMRG","text":"","category":"section"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"The short answer to how to do fully periodic boundary condition DMRG in ITensor is that you simply input a periodic Hamiltonian into our OpSum system and make the MPO form of your Hamiltonian in the usual way. For example, for a chain of N sites with nearest-neighbor interactions, you include a term that connects site 1 to site N. For a one-dimensional Ising model chain Hamiltonian this would look like:","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"sites = siteinds(\"S=1/2\",N)\n\nhterms = OpSum()\nfor j=1:(N-1)\n hterms += \"Sz\",j,\"Sz\",j+1\nend\nhterms += \"Sz\",1,\"Sz\",N # term 'wrapping' around the ring\n\nH = MPO(hterms,sites)","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"For two-dimensional DMRG calculations, where the most common approach is to use periodic boundary conditions in the y-direction only, and not in the x-direction, you do a similar step in making your OpSum input to ITensor DMRG: you include terms wrapping around the periodic cylinder in the y direction but not in the x direction.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"However, fully periodic boundary conditions are only recommended for small systems when absolutely needed, and in general are not recommended. For a longer discussion of alternatives to using fully periodic boundaries, see the next section below.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"The reason fully periodic boundary conditions (periodic in x in 1D, and periodic in both x and y in 2D) are not recommended in general is that the DMRG algorithm, as we are defining it here, optimizes an open-boundary MPS. So if you input a periodic-boundary Hamiltonian, there is a kind of \"mismatch\" that happens where you can still get the correct answer, but it requires much more resources (a larger bond dimension and more sweeps) to get good accuracy. There has been some research into \"truly\" periodic DMRG, [Pippan] that is DMRG that optimizes an MPS with a ring-like topology, but it is not widely used, is still an open area of algorithm development, and is not currently available in ITensor.","category":"page"},{"location":"faq/DMRG.html#What-boundary-conditions-should-I-choose:-open,-periodic,-or-infinite?","page":"DMRG FAQs","title":"What boundary conditions should I choose: open, periodic, or infinite?","text":"","category":"section"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"One of the weaknesses of the density matrix renormalization group (DMRG), and its time-dependent or finite-temperature extensions, is that it works poorly with periodic boundary conditions. This stems from the fact that conventional DMRG optimizes over open-boundary matrix product state (MPS) wavefunctions whether or not the Hamiltonian includes periodic interactions.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"But this begs the question, when are periodic boundary conditions (PBC) really needed? DMRG offers some compelling alternatives to PBC:","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Use open boundary conditions (OBC). Though this introduces edge effects, the number of states needed to reach a given accuracy is significantly lower than with PBC (see next section below). For gapped systems DMRG scales linearly with system size, meaning often one can study systems with many hundreds or even thousands of sites. Last but not least, open boundaries are often more natural. For studying systems which spontaneously break symmetry, adding \"pinning\" fields on the edge is often a very nice way to tip the balance toward a certain symmetry broken state while leaving the bulk unmodified.\nUse smooth boundary conditions. The basic idea is to use OBC but send the Hamiltonian parameters smoothly to zero at the boundary so that the system can not \"feel\" the boundary. For certain systems this can significantly reduce edge effects.[Smooth1][Smooth2][Smooth3]","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Smooth1]: Smooth boundary conditions for quantum lattice systems, M. Vekic and Steven R. White, Phys. Rev. Lett. 71, 4283 (1993) cond-mat/9310053","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Smooth2]: Hubbard model with smooth boundary conditions, M. Vekic and Steven R. White, Phys. Rev. B 53, 14552 (1996) cond-mat/9601009","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Smooth3]: Grand canonical finite-size numerical approaches: A route to measuring bulk properties in an applied field, Chisa Hotta and Naokazu Shibata, Phys. Rev. B 86, 041108 (2012)","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Use \"infinite boundary conditions\", that is, use infinite DMRG in the form of an algorithm like iDMRG or VUMPS. This has a cost that can be even less than with OBC yet is completely free of finite-size effects.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"However, there are a handful of cases where PBC remains preferable despite the extra overhead. A few such cases are:","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Benchmarking DMRG against another code that uses PBC, such as a Monte Carlo or exact diagonalization code.\nExtracting the central charge of a critical one-dimensional system described by a CFT. In practice, using PBC can give an accurate central charge even for quite small systems by fitting the subsystem entanglement entropy to the CFT scaling form.\nChecking for the presence or absence of topological effects. These could be edge effects (the Haldane phase has a four-fold ground state degeneracy with OBC, but not with PBC), or could be related to some global topological sector that is ill-defined with PBC (e.g. periodic vs. antiperiodic boundary conditions for the transverse field Ising model).","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"(Note that in the remaining discussion, by PBC I mean fully periodic boundary conditions in all directions. For the case of DMRG applied to quasi-two-dimensional systems, it remains a good practice to use periodic boundaries in the shorter direction, while still using open (or infinite) boundaries in the longer direction along the DMRG/MPS path.)","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Below I discuss more about the problems with using PBC, as well as some misconceptions about when PBC seems necessary even though there are better alternatives.","category":"page"},{"location":"faq/DMRG.html#Drawbacks-of-Periodic-Boundary-Conditions","page":"DMRG FAQs","title":"Drawbacks of Periodic Boundary Conditions","text":"","category":"section"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Periodic boundary conditions are straightforward to implement in conventional DMRG. The simplest approach is to include a \"long bond\" directly connecting site 1 to site N in the Hamiltonian. However this naive approach has a major drawback: if open-boundary DMRG achieves a given accuracy when keeping m states (bond dimension of size m), then to reach the same accuracy with PBC one must keep closer to m^2 states! The reason is that now every bond of the MPS not only carries local entanglement as with OBC, but also the entanglement between the first and last sites. (There is an alternative DMRG algorithm[Pippan] for periodic systems which may have better scaling than the above approach but has not been widely applied and tested, as far as I am aware, especially for 2D or critical systems .)","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Pippan]: Efficient matrix-product state method for periodic boundary conditions, P. Pippan, Steven R. White, and H.G. Evertz, Phys. Rev. B 81, 081103","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"The change in scaling from m to m^2 is a severe problem. For example, many gapped one-dimensional systems only require about m=100 to reach good accuracy (truncation errors of less than 1E-9 or so). To reach the same accuracy with naive PBC would then require using 10,000 states, which can easily fill the RAM of a typical desktop computer for a large enough system, not to mention the extra time needed to work with larger matrices.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"But poor scaling is not the only drawback of PBC. Systems that exhibit spontaneous symmetry breaking are simple to work with under OBC, where one has the additional freedom of applying edge pinning terms to drive the bulk into a specific symmetry sector. Using edge pinning reduces the bulk entanglement and makes measuring order parameters straightforward. Similarly one can use infinite DMRG to directly observe symmetry breaking effects.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"But under PBC, order parameters remain equal to zero and can only be accessed through correlation functions. Though using correlation functions is often presented as the \"standard\" or \"correct\" approach, such reasoning pre-supposes that PBC is the best choice. Recent work in the quantum Monte Carlo community demonstrates that open boundaries with pinning fields can actually be a superior approach.[Assaad]","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Assaad]: Pinning the Order: The Nature of Quantum Criticality in the Hubbard Model on Honeycomb Lattice, Fakher F. Assaad and Igor F. Herbut, Phys. Rev. X 3, 031010","category":"page"},{"location":"faq/DMRG.html#Cases-Where-Periodic-BC-Seems-Necessary,-But-Open/Infinite-BC-Can-be-Better","page":"DMRG FAQs","title":"Cases Where Periodic BC Seems Necessary, But Open/Infinite BC Can be Better","text":"","category":"section"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Below are some cases where periodic boundary conditions seem to be necessary at a first glance. But in many of these cases, not only can open or infinite boundaries be just as successful, they can even be the better choice.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Measuring asymptotic properties of correlation functions: much of our understanding of gapless one-dimensional systems comes from field-theoretic approaches which make specific predictions about asymptotic decays of various correlators. To test these predictions numerically, one must work with large, translationally invariant systems with minimal edge effects. Using fully periodic boundary conditions satisfies these criteria. However, a superior choice is to use infinite DMRG, which combines the much better scaling of open-boundary DMRG with the ability to measure correlators at arbitrarily long distances by repeating the unit cell of the MPS wavefunction. Although truncating to a finite number of states imposes an effective correlation length on the system, this correlation length can reach many thousands of sites for quite moderate MPS bond dimensions. Karrasch and Moore took advantage of this fact to convincingly check the predictions of Luttinger liquid theory for one-dimensional systems of gapless fermions.[Karrasch]","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Karrasch]: Luttinger liquid physics from the infinite-system density matrix renormalization group, C. Karrasch and J.E. Moore, Phys. Rev. B 86, 155156","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Studying two-dimensional topological order: a hallmark of intrinsic topological order is the presence of a robust ground state degeneracy when the system is put on a torus. Also many topological phases have gapless edge states which can cause problems for numerical calculations. Thus one might think that fully periodic BC are the best choice for studying topological phases. However, topological phases have the same ground-state degeneracy on an infinite cylinder as they do on a torus.[Zhang]. Cincio and Vidal exploited this fact to use infinite DMRG to study a variety of topological phases [Cincio]. One part of their calculation did actually require obtaining ground states on a torus, but they accomplished this by taking a finite segment of an infinite MPS and connecting its ends. This approach does not give the true ground state of the torus but was sufficient for their calculation and was arguably closer to the true two-dimensional physics.","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Zhang]: Quasiparticle statistics and braiding from ground-state entanglement, Yi Zhang, Tarun Grover, Ari Turner, Masaki Oshkawa, and Ashvin Vishwanath, Phys. Rev. B 85, 235151","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Cincio]: Characterizing Topological Order by Studying the Ground States on an Infinite Cylinder, L. Cincio and G. Vidal, Phys. Rev. Lett. 110, 067208","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"Obtaining bulk gaps: DMRG has the ability to \"target\" low-lying excited states or to obtain such states by constraining them to be orthogonal to the ground state. However, with OBC, localized excitations can get stuck to the edges and not reveal the true bulk gap behavior. Thus one may conclude that PBC is necessary. But using open or infinite boundaries remains the better choice because they allow much higher accuracy.\nTo deal with the presence of edges in OBC, one can use \"restricted sweeping\". Here one sweeps across the full system to obtain the ground state. Then, to obtain the first excited state one only sweeps through the full system to obtain the ground state. Then, to obtain the first excited state one only sweeps through the near the edges. This traps the particle in a \"soft box\" which still lets its wavefunction mix with the basis that describes the ground state outside the restricted sweeping region.\nWithin infinite DMRG, boundary effects are rigorously absent if the calculation has converged. To compute bulk gaps one again uses a type of restricted sweeping known in the literature as \"infinite boundary conditions\". For more see the work by Phien, Vidal, and McCulloch.[Phien]","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"[Phien]: Infinite boundary conditions for matrix product state calculations, Ho N. Phien, G. Vidal, and Ian P. McCulloch Phys. Rev. B 86, 245107","category":"page"},{"location":"faq/DMRG.html","page":"DMRG FAQs","title":"DMRG FAQs","text":"In conclusion, consider carefully whether you really need to use periodic boundary conditions, as they impose a steep computational cost within DMRG. Periodic BC can actually be worse for the very types of measurements where they are often presented as the best or \"standard\" choice. Many of the issues periodic boundaries circumvent can be avoided more elegantly by using infinite DMRG, or when that is not applicable, by using open boundary conditions with sufficient care.","category":"page"},{"location":"DMRG.html#DMRG","page":"DMRG","title":"DMRG","text":"","category":"section"},{"location":"DMRG.html","page":"DMRG","title":"DMRG","text":"dmrg","category":"page"},{"location":"DMRG.html#ITensors.ITensorMPS.dmrg","page":"DMRG","title":"ITensors.ITensorMPS.dmrg","text":"dmrg(H::MPO, psi0::MPS; kwargs...)\ndmrg(H::MPO, psi0::MPS, sweeps::Sweeps; kwargs...)\n\nUse the density matrix renormalization group (DMRG) algorithm to optimize a matrix product state (MPS) such that it is the eigenvector of lowest eigenvalue of a Hermitian matrix H, represented as a matrix product operator (MPO).\n\ndmrg(Hs::Vector{MPO}, psi0::MPS; kwargs...)\ndmrg(Hs::Vector{MPO}, psi0::MPS, sweeps::Sweeps; kwargs...)\n\nUse the density matrix renormalization group (DMRG) algorithm to optimize a matrix product state (MPS) such that it is the eigenvector of lowest eigenvalue of a Hermitian matrix H. This version of dmrg accepts a representation of H as a Vector of MPOs, Hs = [H1, H2, H3, ...] such that H is defined as H = H1 + H2 + H3 + ... Note that this sum of MPOs is not actually computed; rather the set of MPOs [H1,H2,H3,..] is efficiently looped over at each step of the DMRG algorithm when optimizing the MPS.\n\ndmrg(H::MPO, Ms::Vector{MPS}, psi0::MPS; weight=1.0, kwargs...)\ndmrg(H::MPO, Ms::Vector{MPS}, psi0::MPS, sweeps::Sweeps; weight=1.0, kwargs...)\n\nUse the density matrix renormalization group (DMRG) algorithm to optimize a matrix product state (MPS) such that it is the eigenvector of lowest eigenvalue of a Hermitian matrix H, subject to the constraint that the MPS is orthogonal to each of the MPS provided in the Vector Ms. The orthogonality constraint is approximately enforced by adding to H terms of the form w|M1>isodd(n) ... is an\n # on-the-fly function mapping integers to strings)\n sites = siteinds(n->isodd(n) ? \"S=1/2\" : \"S=1\",N)\n\n # Couplings between spin-half and\n # spin-one sites:\n Jho = 1.0 # half-one coupling\n Jhh = 0.5 # half-half coupling\n Joo = 0.5 # one-one coupling\n\n os = OpSum()\n for j=1:N-1\n os += 0.5*Jho,\"S+\",j,\"S-\",j+1\n os += 0.5*Jho,\"S-\",j,\"S+\",j+1\n os += Jho,\"Sz\",j,\"Sz\",j+1\n end\n for j=1:2:N-2\n os += 0.5*Jhh,\"S+\",j,\"S-\",j+2\n os += 0.5*Jhh,\"S-\",j,\"S+\",j+2\n os += Jhh,\"Sz\",j,\"Sz\",j+2\n end\n for j=2:2:N-2\n os += 0.5*Joo,\"S+\",j,\"S-\",j+2\n os += 0.5*Joo,\"S-\",j,\"S+\",j+2\n os += Joo,\"Sz\",j,\"Sz\",j+2\n end\n H = MPO(os,sites)\n\n nsweeps = 10\n maxdim = [10,10,20,40,80,100,140,180,200]\n cutoff = [1E-8]\n\n psi0 = randomMPS(sites,4)\n\n energy,psi = dmrg(H,psi0; nsweeps, maxdim, cutoff)\n\n return\nend","category":"page"},{"location":"examples/DMRG.html#Use-a-Sum-of-MPOs-in-DMRG","page":"DMRG Examples","title":"Use a Sum of MPOs in DMRG","text":"","category":"section"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"One version of the ITensor dmrg function accepts an array of MPOs [H1,H2,H3] (or any number of MPOs you want). This version of DMRG will find the ground state of H1+H2+H3. Internally it does not actually sum these MPOs, but loops over them during each step of the \"eigensolver\" at the core of the DMRG algorithm, so it is usually more efficient than if the MPOs had been summed together into a single MPO.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"To use this version of DMRG, say you have MPOs H1, H2, and H3. Then call DMRG like this:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"energy,psi = dmrg([H1,H2,H3],psi0; nsweeps, maxdim, cutoff)","category":"page"},{"location":"examples/DMRG.html#Make-a-2D-Hamiltonian-for-DMRG","page":"DMRG Examples","title":"Make a 2D Hamiltonian for DMRG","text":"","category":"section"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"You can use the OpSum system to make 2D Hamiltonians much in the same way you make 1D Hamiltonians: by looping over all of the bonds and adding the interactions on these bonds to the OpSum.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"To help with the logic of 2D lattices, ITensor pre-defines some helper functions which return an array of bonds. Each bond object has an \"s1\" field and an \"s2\" field which are the integers numbering the two sites the bond connects. (You can view the source for these functions at this link.)","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"The two provided functions currently are square_lattice and triangular_lattice. It is not hard to write your own similar lattice functions as all they have to do is define an array of ITensors.LatticeBond structs or even a custom struct type you wish to define. We welcome any user contributions of other lattices that ITensor does not currently offer.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Each lattice function takes an optional named argument \"yperiodic\" which lets you request that the lattice should have periodic boundary conditions around the y direction, making the geometry a cylinder.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Full example code:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"using ITensors\n\nlet\n Ny = 6\n Nx = 12\n\n N = Nx*Ny\n\n sites = siteinds(\"S=1/2\", N;\n conserve_qns = true)\n\n # Obtain an array of LatticeBond structs\n # which define nearest-neighbor site pairs\n # on the 2D square lattice (wrapped on a cylinder)\n lattice = square_lattice(Nx, Ny; yperiodic = false)\n\n # Define the Heisenberg spin Hamiltonian on this lattice\n os = OpSum()\n for b in lattice\n os .+= 0.5, \"S+\", b.s1, \"S-\", b.s2\n os .+= 0.5, \"S-\", b.s1, \"S+\", b.s2\n os .+= \"Sz\", b.s1, \"Sz\", b.s2\n end\n H = MPO(os,sites)\n\n state = [isodd(n) ? \"Up\" : \"Dn\" for n=1:N]\n # Initialize wavefunction to a random MPS\n # of bond-dimension 10 with same quantum\n # numbers as `state`\n psi0 = randomMPS(sites,state,20)\n\n nsweeps = 10\n maxdim = [20,60,100,100,200,400,800]\n cutoff = [1E-8]\n\n energy,psi = dmrg(H,psi0; nsweeps, maxdim, cutoff)\n\n return\nend","category":"page"},{"location":"examples/DMRG.html#Compute-excited-states-with-DMRG","page":"DMRG Examples","title":"Compute excited states with DMRG","text":"","category":"section"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"ITensor DMRG accepts additional MPS wavefunctions as a optional, extra argument. These additional 'penalty states' are provided as an array of MPS just after the Hamiltonian, like this:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"energy,psi3 = dmrg(H,[psi0,psi1,psi2],psi3_init; nsweeps, maxdim, cutoff)","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Here the penalty states are [psi0,psi1,psi2]. When these are provided, the DMRG code minimizes the energy of the current MPS while also reducing its overlap (inner product) with the previously provided MPS. If these overlaps become sufficiently small, then the computed MPS is an excited state. So by finding the ground state, then providing it to DMRG as a \"penalty state\" or previous state one can compute the first excited state. Then providing both of these, one can get the second excited state, etc.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"A keyword argument called weight can also be provided to the dmrg function when penalizing overlaps to previous states. The weight parameter is multiplied by the overlap with the previous states, so sets the size of the penalty. It should be chosen at least as large as the (estimated) gap between the ground and first excited states. Otherwise the optimal value of the weight parameter is not so obvious, and it is best to try various weights during initial test calculations.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Note that when the system has conserved quantum numbers, a superior way to find excited states can be to find ground states of quantum number (or symmetry) sectors other than the one containing the absolute ground state. In that context, the penalty method used below is a way to find higher excited states within the same quantum number sector.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Full Example code:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"using ITensors\n\nlet\n N = 20\n\n sites = siteinds(\"S=1/2\",N)\n\n h = 4.0\n\n weight = 20*h # use a large weight\n # since gap is expected to be large\n\n\n #\n # Use the OpSum feature to create the\n # transverse field Ising model\n #\n # Factors of 4 and 2 are to rescale\n # spin operators into Pauli matrices\n #\n os = OpSum()\n for j=1:N-1\n os += -4,\"Sz\",j,\"Sz\",j+1\n end\n for j=1:N\n os += -2*h,\"Sx\",j;\n end\n H = MPO(os,sites)\n\n\n #\n # Make sure to do lots of sweeps\n # when finding excited states\n #\n nsweeps = 30\n maxdim = [10,10,10,20,20,40,80,100,200,200]\n cutoff = [1E-8]\n noise = [1E-6]\n\n #\n # Compute the ground state psi0\n #\n psi0_init = randomMPS(sites,linkdims=2)\n energy0,psi0 = dmrg(H,psi0_init; nsweeps, maxdim, cutoff, noise)\n\n println()\n\n #\n # Compute the first excited state psi1\n #\n psi1_init = randomMPS(sites,linkdims=2)\n energy1,psi1 = dmrg(H,[psi0],psi1_init; nsweeps, maxdim, cutoff, noise, weight)\n\n # Check psi1 is orthogonal to psi0\n @show inner(psi1,psi0)\n\n\n #\n # The expected gap of the transverse field Ising\n # model is given by Eg = 2*|h-1|\n #\n # (The DMRG gap will have finite-size corrections)\n #\n println(\"DMRG energy gap = \",energy1-energy0);\n println(\"Theoretical gap = \",2*abs(h-1));\n\n println()\n\n #\n # Compute the second excited state psi2\n #\n psi2_init = randomMPS(sites,linkdims=2)\n energy2,psi2 = dmrg(H,[psi0,psi1],psi2_init; nsweeps, maxdim, cutoff, noise, weight)\n\n # Check psi2 is orthogonal to psi0 and psi1\n @show inner(psi2,psi0)\n @show inner(psi2,psi1)\n\n return\nend","category":"page"},{"location":"examples/DMRG.html#Printing-the-Entanglement-Entropy-at-Each-Step","page":"DMRG Examples","title":"Printing the Entanglement Entropy at Each Step","text":"","category":"section"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"To obtain the entanglement entropy of an MPS at each step during a DMRG calculation, you can use the Observer system to make a custom observer object that prints out this information.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"First we define our custom observer type, EntanglementObserver, and overload the measure! function for it:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"mutable struct EntanglementObserver <: AbstractObserver\nend\n\nfunction ITensors.measure!(o::EntanglementObserver; bond, psi, half_sweep, kwargs...)\n wf_center, other = half_sweep==1 ? (psi[bond+1],psi[bond]) : (psi[bond],psi[bond+1])\n U,S,V = svd(wf_center, uniqueinds(wf_center,other))\n SvN = 0.0\n for n=1:dim(S, 1)\n p = S[n,n]^2\n SvN -= p * log(p)\n end\n println(\" Entanglement across bond $bond = $SvN\")\nend","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"The measure! function grabs certain helpful keywords passed to it by DMRG, such as what bond DMRG has just finished optimizing.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Here is a complete sample code including constructing the observer and passing it to DMRG:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"using ITensors\n\nmutable struct EntanglementObserver <: AbstractObserver\nend\n\nfunction ITensors.measure!(o::EntanglementObserver; bond, psi, half_sweep, kwargs...)\n wf_center, other = half_sweep==1 ? (psi[bond+1],psi[bond]) : (psi[bond],psi[bond+1])\n U,S,V = svd(wf_center, uniqueinds(wf_center,other))\n SvN = 0.0\n for n=1:dim(S, 1)\n p = S[n,n]^2\n SvN -= p * log(p)\n end\n println(\" Entanglement across bond $bond = $SvN\")\nend\n\nlet\n N = 100\n\n s = siteinds(\"S=1/2\",N)\n\n a = OpSum()\n for n=1:N-1\n a += \"Sz\",n,\"Sz\",n+1\n a += 0.5,\"S+\",n,\"S-\",n+1\n a += 0.5,\"S-\",n,\"S+\",n+1\n end\n H = MPO(a,s)\n psi0 = randomMPS(s,linkdims=4)\n\n nsweeps = 5\n maxdim = [10,20,80,160]\n cutoff = 1E-8\n\n observer = EntanglementObserver()\n\n energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff, observer, outputlevel=2)\n\n return\nend","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Example output:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"...\nSweep 2, half 2, bond (35,36) energy=-44.08644657103751\n Truncated using cutoff=1.0E-08 maxdim=20 mindim=1\n Trunc. err=2.54E-07, bond dimension 20\n Entanglement across bond 35 = 0.7775882479059774\nSweep 2, half 2, bond (34,35) energy=-44.086696891668424\n Truncated using cutoff=1.0E-08 maxdim=20 mindim=1\n Trunc. err=2.12E-07, bond dimension 20\n Entanglement across bond 34 = 0.7103532704635472\nSweep 2, half 2, bond (33,34) energy=-44.08696190368391\n Truncated using cutoff=1.0E-08 maxdim=20 mindim=1\n Trunc. err=1.29E-07, bond dimension 20\n Entanglement across bond 33 = 0.7798362911744212\n...","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"If you only want to see the maximum entanglement during each sweep, you can add a field to the EntanglementObserver object that saves the maximum value encountered so far and keep overwriting this field, printing out the most recently observed maximum at the end of each sweep.","category":"page"},{"location":"examples/DMRG.html#Monitoring-the-Memory-Usage-of-DMRG","page":"DMRG Examples","title":"Monitoring the Memory Usage of DMRG","text":"","category":"section"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"To monitor how much memory (RAM) a DMRG calculation is using while it is running, you can use the Observer system to make a custom observer object that prints out this information. Also the Base.summarysize function, which returns the size in bytes of any Julia object is very helpful here.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"First we define our custom observer type, SizeObserver, and overload the measure! function for it:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"mutable struct SizeObserver <: AbstractObserver\nend\n\nfunction ITensors.measure!(o::SizeObserver; bond, half_sweep, psi, projected_operator, kwargs...)\n if bond==1 && half_sweep==2\n psi_size = Base.format_bytes(Base.summarysize(psi))\n PH_size = Base.format_bytes(Base.summarysize(projected_operator))\n println(\"|psi| = $psi_size, |PH| = $PH_size\")\n end\nend","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"The measure! function grabs certain helpful keywords passed to it by DMRG, checking if bond==1 && half_sweep==2 so that it only runs when at the end of a full sweep.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"When it runs, it calls Base.summarysize on the wavefunction psi object and the projected_operator object. The projected_operator, which is the matrix (Hamiltonian) wrapped into the current MPS basis, is usually the largest-sized object in a DMRG calculation. The code also uses Base.format_bytes to turn an integer representing bytes into a human-readable string.","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Here is a complete sample code including constructing the observer and passing it to DMRG:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"using ITensors\n\nmutable struct SizeObserver <: AbstractObserver\nend\n\nfunction ITensors.measure!(o::SizeObserver; bond, sweep, half_sweep, psi, projected_operator, kwargs...)\n if bond==1 && half_sweep==2\n psi_size = Base.format_bytes(Base.summarysize(psi))\n PH_size = Base.format_bytes(Base.summarysize(projected_operator))\n println(\"After sweep $sweep, |psi| = $psi_size, |PH| = $PH_size\")\n end\nend\n\nlet\n N = 100\n\n s = siteinds(\"S=1/2\",N)\n\n a = OpSum()\n for n=1:N-1\n a += \"Sz\",n,\"Sz\",n+1\n a += 0.5,\"S+\",n,\"S-\",n+1\n a += 0.5,\"S-\",n,\"S+\",n+1\n end\n H = MPO(a,s)\n psi0 = randomMPS(s,linkdims=4)\n\n nsweeps = 5\n maxdim = [10,20,80,160]\n cutoff = 1E-8\n\n obs = SizeObserver()\n\n energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff, observer=obs)\n\n return\nend","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"Example output:","category":"page"},{"location":"examples/DMRG.html","page":"DMRG Examples","title":"DMRG Examples","text":"After sweep 1, |psi| = 211.312 KiB, |PH| = 593.984 KiB\nAfter sweep 1 energy=-43.95323393592883 maxlinkdim=10 maxerr=8.26E-06 time=0.098\nAfter sweep 2, |psi| = 641.000 KiB, |PH| = 1.632 MiB\nAfter sweep 2 energy=-44.10791340895817 maxlinkdim=20 maxerr=7.39E-07 time=0.132\nAfter sweep 3, |psi| = 1.980 MiB, |PH| = 5.066 MiB\nAfter sweep 3 energy=-44.12593605906466 maxlinkdim=44 maxerr=9.96E-09 time=0.256\nAfter sweep 4, |psi| = 2.863 MiB, |PH| = 7.246 MiB\nAfter sweep 4 energy=-44.127710946536645 maxlinkdim=56 maxerr=9.99E-09 time=0.445\nAfter sweep 5, |psi| = 3.108 MiB, |PH| = 7.845 MiB\nAfter sweep 5 energy=-44.127736798226536 maxlinkdim=57 maxerr=9.98E-09 time=0.564","category":"page"},{"location":"Observer.html#observer","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"","category":"section"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"An observer is an object which can be passed to the ITensor DMRG algorithm, to allow measurements to be performed throughout the DMRG calculation and to set conditions for early stopping of DMRG.","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"The only requirement of an observer is that it is a subtype of AbstractObserver. But to do something interesting, it should also overload at least one the methods measure! or checkdone!.","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"A general purpose observer type called DMRGObserver is included with ITensors which already provides some quite useful features. It accepts a list of strings naming local operators to be measured at each step of DMRG, with the results saved for later analysis. It also accepts an optional energy precision, and stops a DMRG calculation early if the energy no longer changes to this precision. For more details about the DMRGObserver type, see the DMRGObserver documentation page.","category":"page"},{"location":"Observer.html#Defining-a-Custom-Observer","page":"Observer System for DMRG","title":"Defining a Custom Observer","text":"","category":"section"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"To define a custom observer, just make a struct with any name and internal fields you would like, and make this struct a subtype of AbstractObserver.","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"For example, let's make a type called DemoObserver as:","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"mutable struct DemoObserver <: AbstractObserver\n energy_tol::Float64\n last_energy::Float64\n\n DemoObserver(energy_tol=0.0) = new(energy_tol,1000.0)\nend\n","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"In this minimal example, our DemoObserver contains a field energy_tol which we can use to set an early-stopping condition for DMRG, and an field last_energy which our observer will use internally to keep track of changes to the energy after each sweep.","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"Now to give our DemoObserver type a useful behavior we need to define overloads of the methods measure! and checkdone!.","category":"page"},{"location":"Observer.html#Overloading-the-checkdone!-method","page":"Observer System for DMRG","title":"Overloading the checkdone! method","text":"","category":"section"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"Let's start with the checkdone! method. After each sweep of DMRG, the checkdone! method is passed the observer object, as well as a set of keyword arguments which currently include:","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"energy: the current energy\npsi: the current wavefunction MPS\nsweep: the number of the sweep that just finished\noutputlevel: an integer stating the desired level of output","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"If the checkdone! function returns true, then the DMRG routine stops (recall that checkdone! is called only at the end of a sweep).","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"In our example, we will just compare the energy keyword argument to the last_energy variable held inside the DemoObserver:","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"function ITensors.checkdone!(o::DemoObserver;kwargs...)\n sw = kwargs[:sweep]\n energy = kwargs[:energy]\n if abs(energy-o.last_energy)/abs(energy) < o.energy_tol\n println(\"Stopping DMRG after sweep $sw\")\n return true\n end\n # Otherwise, update last_energy and keep going\n o.last_energy = energy\n return false\nend","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"(Recall that in order to properly overload the default behavior, the checkdone! method has to be imported from the ITensors module or preceded with ITensors.)","category":"page"},{"location":"Observer.html#Overloading-the-measure!-method","page":"Observer System for DMRG","title":"Overloading the measure! method","text":"","category":"section"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"The other method that an observer can overload is measure!. This method is called at every step of DMRG, so at every site and for every sweep. The measure! method is passed the current observer object and a set of keyword arguments which include:","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"energy: the energy after the current step of DMRG\npsi: the current wavefunction MPS\nbond: the bond b that was just optimized, corresponding to sites (b,b+1) in the two-site DMRG algorithm\nsweep: the current sweep number\nsweep_is_done: true if at the end of the current sweep, otherwise false\nhalf_sweep: the half-sweep number, equal to 1 for a left-to-right, first half sweep, or 2 for the second, right-to-left half sweep\nspec: the Spectrum object returned from factorizing the local superblock wavefunction tensor in two-site DMRG\noutputlevel: an integer specifying the amount of output to show\nprojected_operator: projection of the linear operator into the current MPS basis","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"For our minimal DemoObserver example here, we will just make a measure! function that prints out some of the information above, but in a more realistic setting one could use the MPS psi to perform essentially arbitrary measurements.","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"function ITensors.measure!(o::DemoObserver; kwargs...)\n energy = kwargs[:energy]\n sweep = kwargs[:sweep]\n bond = kwargs[:bond]\n outputlevel = kwargs[:outputlevel]\n\n if outputlevel > 0\n println(\"Sweep $sweep at bond $bond, the energy is $energy\")\n end\nend","category":"page"},{"location":"Observer.html#Calling-DMRG-with-the-Custom-Observer","page":"Observer System for DMRG","title":"Calling DMRG with the Custom Observer","text":"","category":"section"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"After defining an observer type and overloading at least one of the methods checkdone! or measure! for it, one can construct an object of this type and pass it to the ITensor dmrg function using the observer keyword argument.","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"Continuing with our DemoObserver example above:","category":"page"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"obs = DemoObserver(1E-4) # use an energy tolerance of 1E-4\nenergy, psi = dmrg(H,psi0,sweeps; observer=obs, outputlevel=1)","category":"page"},{"location":"Observer.html#Complete-Sample-Code","page":"Observer System for DMRG","title":"Complete Sample Code","text":"","category":"section"},{"location":"Observer.html","page":"Observer System for DMRG","title":"Observer System for DMRG","text":"using ITensors\n\nmutable struct DemoObserver <: AbstractObserver\n energy_tol::Float64\n last_energy::Float64\n\n DemoObserver(energy_tol=0.0) = new(energy_tol,1000.0)\nend\n\nfunction ITensors.checkdone!(o::DemoObserver;kwargs...)\n sw = kwargs[:sweep]\n energy = kwargs[:energy]\n if abs(energy-o.last_energy)/abs(energy) < o.energy_tol\n println(\"Stopping DMRG after sweep $sw\")\n return true\n end\n # Otherwise, update last_energy and keep going\n o.last_energy = energy\n return false\nend\n\nfunction ITensors.measure!(o::DemoObserver; kwargs...)\n energy = kwargs[:energy]\n sweep = kwargs[:sweep]\n bond = kwargs[:bond]\n outputlevel = kwargs[:outputlevel]\n\n if outputlevel > 0\n println(\"Sweep $sweep at bond $bond, the energy is $energy\")\n end\nend\n\nlet\n N = 10\n etol = 1E-4\n\n s = siteinds(\"S=1/2\",N)\n\n a = OpSum()\n for n=1:N-1\n a += \"Sz\",n,\"Sz\",n+1\n a += 0.5,\"S+\",n,\"S-\",n+1\n a += 0.5,\"S-\",n,\"S+\",n+1\n end\n H = MPO(a,s)\n psi0 = randomMPS(s,4)\n\n nsweeps = 5\n cutoff = 1E-8\n maxdim = [10,20,100]\n\n obs = DemoObserver(etol)\n\n println(\"Starting DMRG\")\n energy, psi = dmrg(H,psi0; nsweeps, cutoff, maxdim, observer=obs, outputlevel=1)\n\n return\nend","category":"page"},{"location":"examples/ITensor.html#itensor_examples","page":"ITensor Examples","title":"ITensor Code Examples","text":"","category":"section"},{"location":"examples/ITensor.html#Print-Indices-of-an-ITensor","page":"ITensor Examples","title":"Print Indices of an ITensor","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Sometimes the printout of an ITensor can be rather large, whereas you might only want to see its indices. For these cases, just wrap the ITensor in the function inds like this:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"@show inds(T)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"or this","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"println(\"T inds = \",inds(T))","category":"page"},{"location":"examples/ITensor.html#Getting-and-Setting-Elements-of-an-ITensor","page":"ITensor Examples","title":"Getting and Setting Elements of an ITensor","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Say we have an ITensor constructed as:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"i = Index(3,\"index_i\")\nj = Index(2,\"index_j\")\nk = Index(4,\"index_k\")\n\nT = ITensor(i,j,k)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"An ITensor constructed this way starts with all of its elements equal to zero. (Technically it allocates no storage at all but this is an implementation detail.)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Setting Elements","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To set an element of this ITensor, such as the element where (i,j,k) = (2,1,3), you can do the following:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"T[i=>2,j=>1,k=>3] = -3.2","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"In the Julia language, the notation a=>b is a built-in notation for making a Pair(a,b) object.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Because the Index objects are passed to T along with their values, passing them in a different order has exactly the same effect:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"# Both of these lines of code do the same thing:\nT[j=>1,i=>2,k=>3] = -3.2\nT[j=>1,k=>3,i=>2] = -3.2","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Getting Elements","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"You can retrieve individual elements of an ITensor by accessing them through the same notation used to set elements:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"el = T[j=>1,i=>2,k=>3]\nprintln(\"The (i,j,k) = (2,1,3) element of T is \",el)","category":"page"},{"location":"examples/ITensor.html#Making-ITensors-from-Arrays","page":"ITensor Examples","title":"Making ITensors from Arrays","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To initialize all of the elements of an ITensor at once, you can pass a Julia array into the ITensor constructor.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"For example, if we want to construct an ITensor A with indices i,j we can initialize it from a matrix as follows:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"M = [1.0 2.0;\n 3.0 4.0]\n\ni = Index(2,\"i\")\nj = Index(2,\"j\")\n\nA = ITensor(M,i,j)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"More generally we can use an nth-order (n-dimensional) Julia array to initialize an ITensor:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"T = randn(4,7,2)\n\nk = Index(4,\"index_k\")\nl = Index(7,\"index_l\")\nm = Index(2,\"index_m\")\n\nB = ITensor(T,k,l,m)","category":"page"},{"location":"examples/ITensor.html#Making-Arrays-from-ITensors","page":"ITensor Examples","title":"Making Arrays from ITensors","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Not only can we make an ITensor from a Julia array, but we can also convert an ITensor back into a Julia array.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Say we have made an ITensor with two indices:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"using ITensors # hide\nk = Index(4,\"index_k\")\nm = Index(2,\"index_m\")\n\nT = randomITensor(k,m)\n@show T\ndisplay(T) # hide","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Here we used the randomITensor constructor to fill T with random elements but we could make an ITensor some other way too.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Now to convert T into a regular Julia array A, use the Array constructor and pass the indices of T in the order that you want:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"A = Array(T,k,m)\n@show A","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"The reason you have to pass the indices is that the ordering of ITensor indices is an implementation detail and not part of the user interface. So when leaving the ITensor system and converting to a regular array, you must say what ordering of the indices you want. Making the array as A = Array(T,m,k) would give the transpose of the array in the code above.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Note that for efficiency reasons, the array returned by the array function will sometimes be a view of the ITensor, such that changing an element of A would also change the corresponding element of T. This is not always the case though: for example if the indices are passed in a different order from how the internal ITensor storage is arranged, or if T is a block-sparse ITensor, since the (not stored) zero blocks will need to be filled in.","category":"page"},{"location":"examples/ITensor.html#Arithmetic-With-ITensors","page":"ITensor Examples","title":"Arithmetic With ITensors","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"ITensors can be added and subtracted and multiplied by scalars just like plain tensors can. But ITensors have the additional feature that you can add and subtract them even if their indices are in a different order from each other, as long as they have the same collection of indices.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"For example, say we have ITensors A, B, and C:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"i = Index(3,\"i\")\nj = Index(2,\"j\")\nk = Index(4,\"k\")\n\nA = randomITensor(i,j,k)\nB = randomITensor(i,j,k)\nC = randomITensor(k,i,j)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Above we have initialized these ITensors to have random elements, just for the sake of this example.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"We can then add or subtract these ITensors","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"R1 = A + B\nR2 = A - B\nR3 = A + B - C","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"or do more complicated operations involving real and complex scalars too:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"R4 = 2.0*A - B + C/(1+1im)","category":"page"},{"location":"examples/ITensor.html#Elementwise-Operations-on-ITensors","page":"ITensor Examples","title":"Elementwise Operations on ITensors","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"[Note: currently elementwise operations are only defined for dense ITensors, not for block-sparse QN ITensors.]","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"ITensors support Julia broadcasting operations, making it quite easy to carry out element-wise operations on them in a very similar way as for regular Julia arrays. As a concrete example, consider the following ITensor initialized with random elements","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"i = Index(2,\"i\")\nj = Index(3,\"j\")\n\nA = randomITensor(i,j)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Here are some examples of basic element-wise operations we can do using Julia's dotted operator broadcasting syntax.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"# Multiply every element of `A` by 2.0:\nA .*= 2.0","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"# Add 1.5 to every element of A\nA .+= 1.5","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"The dotted notation works for functions too:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"# Replace every element in A by its absolute value:\nA .= abs.(A)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"# Replace every element in A by the number 1.0\nA .= one.(A)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"If have another ITensor B = ITensor(j,i), which has the same set of indices though possibly in a different order, then we can also do element-wise operations involving both ITensors:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"# Add elements of A and B element-wise\nA .= A .+ B\n# Add elements of A and B element-wise with coefficients included\nA .= (2.0 .* A) .+ (-3.0 .* B)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Last but not least, it is possible to make custom functions yourself and broadcast them across elements of ITensors:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"myf(x) = 1.0/(1.0+exp(-x))\nT .= myf.(T)","category":"page"},{"location":"examples/ITensor.html#Making-an-ITensor-with-a-Single-Non-Zero-Element","page":"ITensor Examples","title":"Making an ITensor with a Single Non-Zero Element","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"It is often useful to make ITensors with all elements zero except for a specific element that is equal to 1.0. Use cases can include making product-state quantum wavefunctions or contracting single-element ITensors with other ITensors to set their indices to a fixed value.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To make such an ITensor, use the onehot function. Borrowing terminology from engineering, a \"one hot\" vector or tensor has a single element equal to 1.0 and the rest zero. (In previous versions of ITensor this function was called setelt.)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"The ITensor function onehot takes one or more Index-value Pairs such as i=>2 and j=>1 and returns an ITensor with a 1.0 in the location specified by the Index values:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"using ITensors # hide\ni = Index(2)\nO1 = onehot(i=>1)\nprintln(O1)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"using ITensors # hide\ni = Index(2) # hide\nO2 = onehot(i=>2)\nprintln(O2)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"using ITensors # hide\ni = Index(2) # hide\nj = Index(3)\nT = onehot(i=>2,j=>3)\nprintln(T)","category":"page"},{"location":"examples/ITensor.html#Tracing-an-ITensor","page":"ITensor Examples","title":"Tracing an ITensor","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"An important operation involving a single tensor is tracing out certain pairs of indices. Say we have an ITensor A with indices i,j,l:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"i = Index(4,\"i\")\nj = Index(3,\"j\")\nl = Index(4,\"l\")\n\nA = randomITensor(i,j,l)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"and we want to trace A by summing over the indices i and l locked together, in other words: sum_i A^iji.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To do this in ITensor, we can use a delta tensor, which you can think of as an identity operator or more generally a Kronecker delta or \"hyper-edge\":","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"(Image: )","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Viewed as an array, a delta tensor has all diagonal elements equal to 1.0 and zero otherwise.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Now we can compute the trace by contracting A with the delta tensor:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"trA = A * delta(i,l)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"(Image: )","category":"page"},{"location":"examples/ITensor.html#Factoring-ITensors-(SVD,-QR,-etc.)","page":"ITensor Examples","title":"Factoring ITensors (SVD, QR, etc.)","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"The ITensor approach to tensor factorizations emphasizes the structure of the factorization, and does not require knowing the index ordering.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"ITensor offers various tensor factorizations, such as the singular value decomposition (SVD) and the QR factorization. These are extended to the case of tensors by treating some of the indices as the \"row\" indices and the rest of the indices as the \"column\" indices, reshaping the tensor into a matrix to carry out the factorization, then restoring the tensor structure at the end. All of these steps are done for you by the ITensor system as we will see below.","category":"page"},{"location":"examples/ITensor.html#Singular-Value-Decomposition","page":"ITensor Examples","title":"Singular Value Decomposition","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"The singular value decomposition (SVD) is a matrix factorization that is also extremely useful for general tensors.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"As a brief review, the SVD is a factorization of a matrix M into the product","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"M = U S V^dagger","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"with U and V having the property U^dagger U = 1 and V^dagger V = 1. The matrix S is diagonal and has real, non-negative entries known as the singular values, which are typically ordered from largest to smallest. The SVD is well-defined for any matrix, including rectangular matrices. It also leads to a controlled approximation, where the error due to discarding columns of U and V is small if the corresponding singular values discarded are small.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To compute the SVD of an ITensor, you only need to specify which indices are (collectively) the \"row\" indices (thinking of the ITensor as a matrix), with the rest assumed to be the \"column\" indices.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Say we have an ITensor with indices i,j, and k","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"T = ITensor(i,j,k)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"and we want to treat i and k as the \"row\" indices for the purpose of the SVD.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To perform this SVD, we can call the function svd as follows:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"U,S,V = svd(T,(i,k))","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Diagrammatically the SVD operation above looks like:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"(Image: )","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"The guarantee of the svd function is that the ITensor product U*S*V gives us back an ITensor identical to T:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"@show norm(U*S*V - T) # typical output: norm(U*S*V-T) = 1E-14","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Full working example:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"i = Index(3,\"i\")\nj = Index(4,\"j\")\nk = Index(5,\"k\")\n\nT = randomITensor(i,j,k)\n\nU,S,V = svd(T,(i,k))\n\n@show norm(U*S*V-T)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Truncated SVD","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"An important use of the SVD is approximating a higher-rank tensor by a product of lower-rank tensors whose indices range over only a modest set of values.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To obtain an approximate SVD in ITensor, pass one or more of the following accuracy parameters as named arguments:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"cutoff –- real number epsilon. Discard the smallest singular values lambda_n such that the truncation error is less than epsilon: $ \\frac{\\sum_{n\\in\\text{discarded}} \\lambda^2_n}{\\sum_{n} \\lambda^2_n} < \\epsilon \\:. $ Using a cutoff allows the SVD algorithm to truncate as many states as possible while still ensuring a certain accuracy.\nmaxdim –- integer M. If the number of singular values exceeds M, only the largest M will be retained.\nmindim –- integer m. At least m singular values will be retained, even if some fall below the cutoff","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Let us revisit the example above, but also provide some of these accuracy parameters","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"i = Index(10,\"i\")\nj = Index(40,\"j\")\nk = Index(20,\"k\")\nT = randomITensor(i,j,k)\n\nU,S,V = svd(T,(i,k),cutoff=1E-2)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Note that we have also made the indices larger so that the truncation performed will be non-trivial. In the code above, we specified that a cutoff of epsilon=10^-2 be used. We can check that the resulting factorization is now approximate by computing the squared relative error:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"truncerr = (norm(U*S*V - T)/norm(T))^2\n@show truncerr\n# typical output: truncerr = 8.24E-03","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Note how the computed error is below the cutoff epsilon we requested.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Full working example including truncation:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"i = Index(10,\"i\");\nj = Index(40,\"j\");\nk = Index(20,\"k\");\n\nT = randomITensor(i,j,k)\n\nU,S,V = svd(T,(i,k),cutoff=1E-2)\n\n@show norm(U*S*V-T)\n@show (norm(U*S*V - T)/norm(T))^2","category":"page"},{"location":"examples/ITensor.html#QR-Factorization","page":"ITensor Examples","title":"QR Factorization","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Computing the QR factorization of an ITensor works in a similar way as for the SVD. In addition to passing the ITensor you want to factorize, you must also pass the indices you want to end up on the tensor Q, in other words to be treated as the \"row\" indices for the purpose of defining the QR factorization.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Say we want to compute the QR factorization of an ITensor T with indices i,j,k, putting the indices i and k onto Q and the remaining indices onto R. We can do this as follows:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"(Image: )","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"T = randomITensor(i,j,k)\nQ,R = qr(T,(i,k);positive=true)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Note the use of the optional positive=true keyword argument, which ensures that the diagonal elements of R are non-negative. With this option, the QR factorization is unique, which can be useful in certain cases.","category":"page"},{"location":"examples/ITensor.html#Combining-Multiple-Indices-into-One-Index","page":"ITensor Examples","title":"Combining Multiple Indices into One Index","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"It can be very useful to combine or merge multiple indices of an ITensor into a single Index. Say we have an ITensor with indices i,j,k and we want to combine Index i and Index k into a new Index. This new Index (call it c) will have a dimension whose size is the dimension of i times the dimension of k.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To carry out this procedure we can make a special kind of ITensor: a combiner. To make a combiner, call the function combiner, passing the indices you want to combine:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"using ITensors # hide\ni = Index(4,\"i\") # hide\nj = Index(3,\"j\") # hide\nk = Index(2,\"k\") # hide\nC = combiner(i,k; tags=\"c\")\nnothing # hide","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Then if we have an ITensor","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"T = randomITensor(i,j,k)\n@show inds(T)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"we can combine indices i and k by contracting with the combiner:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"CT = C * T\nnothing # hide","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Printing out the indices of the new ITensor CT we can see that it has only two indices:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"@show inds(CT)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"The first is the newly made combined Index, which was made for us by the combiner function and the second is the j Index of T which was not part of the combining process. To access the combined Index you can call the combinedind function on the combiner:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"ci = combinedind(C)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"We can visualize all of the steps above as follows: (Image: )","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Combining is not limited to two indices and you can combine any number of indices, in any order, using a combiner.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"To undo the combining process and uncombine the Index c back into i,k, just contract with the conjugate of the combiner ITensor dag(C).","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"UT = dag(C) * CT\n@show inds(UT)","category":"page"},{"location":"examples/ITensor.html#Write-and-Read-an-ITensor-to-Disk-with-HDF5","page":"ITensor Examples","title":"Write and Read an ITensor to Disk with HDF5","text":"","category":"section"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"info: Info\nMake sure to install the HDF5 package to use this feature. (Run julia> ] add HDF5 in the Julia REPL console.)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Saving ITensors to disk can be very useful. For example, you might encounter a bug in your own code, and by reading the ITensors involved from disk you can shortcut the process of running a lengthy algorithm over many times to reproduce the bug. Or you can save the output of an expensive calculation, such as a DMRG calculation, and use it as a starting point for multiple follow-up calculations such as computing time-dependent properties.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"ITensors can be written to files using the HDF5 format. HDF5 offers many benefits such as being portable across different machine types, and offers a standard interface across various libraries and languages.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Writing an ITensor to an HDF5 File","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Let's say you have an ITensor T which you have made or obtained from a calculation. To write it to an HDF5 file named \"myfile.h5\" you can use the following pattern:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"using HDF5\nf = h5open(\"myfile.h5\",\"w\")\nwrite(f,\"T\",T)\nclose(f)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Above, the string \"T\" can actually be any string you want such as \"ITensor T\" or \"Result Tensor\" and doesn't have to have the same name as the reference T. Closing the file f is optional and you can also write other objects to the same file before closing it.","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Reading an ITensor from an HDF5 File","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Say you have an HDF5 file \"myfile.h5\" which contains an ITensor stored as a dataset with the name \"T\". (Which would be the situation if you wrote it as in the example above.) To read this ITensor back from the HDF5 file, use the following pattern:","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"using HDF5\nf = h5open(\"myfile.h5\",\"r\")\nT = read(f,\"T\",ITensor)\nclose(f)","category":"page"},{"location":"examples/ITensor.html","page":"ITensor Examples","title":"ITensor Examples","text":"Note the ITensor argument to the read function, which tells Julia which read function to call and how to interpret the data stored in the HDF5 dataset named \"T\". In the future we might lift the requirement of providing the type and have it be detected automatically from the data stored in the file.","category":"page"},{"location":"examples/Physics.html#Physics-(SiteType)-System-Examples","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"","category":"section"},{"location":"examples/Physics.html#Obtaining-a-Predefined-Operator","page":"Physics (SiteType) System Examples","title":"Obtaining a Predefined Operator","text":"","category":"section"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Given an Index carrying a \"physical\" tag such as \"Qubit\", \"S=1/2\", \"Boson\", etc. there are a set of pre-defined operators for each tag. The entire set of operators can be found in the section SiteTypes Included with ITensor.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"If you have an Index s carrying a \"S=1/2\" tag, for example, you can obtain the \"Sz\" operator like this:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"op(\"Sz\",s)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Usually indices with physical tags come from an array of indices returned from the siteinds function","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"sites = siteinds(\"S=1/2\",N)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"in which case one might want the \"Sz\" operator on site 4","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Sz4 = op(\"Sz\",sites[4])","category":"page"},{"location":"examples/Physics.html#Make-a-Custom-Operator-from-a-Matrix","page":"Physics (SiteType) System Examples","title":"Make a Custom Operator from a Matrix","text":"","category":"section"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The op function can be passed any matrix, as long as it has the correct dimensions, and it will make this into an ITensor representing the operator with the corresponding matrix elements.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"For example, if we have a two-dimensional Index s we could make the \"Sz\" operator ourselves from the matrix","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"M = [1/2 0 ; 0 -1/2]","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"by calling","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Sz = op(M,s)","category":"page"},{"location":"examples/Physics.html#custom_op","page":"Physics (SiteType) System Examples","title":"Making a Custom op Definition","text":"","category":"section"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The function op is used to obtain operators defined for a given \"site type\". ITensor includes pre-defined site types such as \"S=1/2\", \"S=1\", \"Electron\" and others. Or you can define your own site type as discussed in detail in the code examples further below.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Extending op Function Definitions","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Perhaps the most common part of the site type system one wishes to extend are the various op or op! function overloads which allow code like","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"s = siteind(\"S=1/2\")\nSz = op(\"Sz\",s)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"to automatically create the S^z operator for an Index s based on the \"S=1/2\" tag it carries. A major reason to define such op overloads is to allow the OpSum system to recognize new operator names, as discussed more below.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Let's see how to introduce a new operator name into the ITensor site type system for this existing site type of \"S=1/2\". The operator we will introduce is the projector onto the up spin state P_uparrow which we will denote with the string \"Pup\".","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"As a matrix acting on the space uparrowrangle downarrowrangle , the P_uparrow operator is given by","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"beginaligned\n\nP_uparrow =\nbeginbmatrix\n 1 0 \n 0 0 \nendbmatrix\n\nendaligned","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"To add this operator to the ITensor op system, we just need to introduce the following code","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"using ITensors\n\nITensors.op(::OpName\"Pup\",::SiteType\"S=1/2\") =\n [1 0\n 0 0]","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"This code can be defined anywhere, such as in your own personal application code and does not have to be put into the ITensor library source code.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Note that we have to name the function ITensors.op and not just op so that it overloads other functions of the name op inside the ITensors module.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Having defined the above code, we can now do things like","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"s = siteind(\"S=1/2\")\nPup = op(\"Pup\",s)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"to obtain the \"Pup\" operator for our \"S=1/2\" Index s. Or we can do a similar thing for an array of site indices:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"N = 40\ns = siteinds(\"S=1/2\",N)\nPup1 = op(\"Pup\",s[1])\nPup3 = op(\"Pup\",s[3])","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Note that for the \"Qudit\"/\"Boson\" site types, you have to define your overload of op with the dimension of the local Hilbert space, for example:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"using ITensors\n\nfunction ITensors.op(::OpName\"P1\", ::SiteType\"Boson\", d::Int)\n o = zeros(d, d)\n o[1, 1] = 1\n return o\nend","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Alternatively you could use Julia's array comprehension syntax:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"ITensors.op(::OpName\"P1\", ::SiteType\"Boson\", d::Int) =\n [(i == j == 1) ? 1.0 : 0.0 for i in 1:d, j in 1:d]","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Using Custom Operators in OpSum","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"A key use of these op system extensions is allowing additional operator names to be recognized by the OpSum system for constructing matrix product operator (MPO) tensor networks. With the code above defining the \"Pup\" operator, we are now allowed to use this operator name in any OpSum code involving \"S=1/2\" site indices.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"For example, we could now make an OpSum involving our custom operator such as:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"N = 100\nsites = siteinds(\"S=1/2\",N)\nos = OpSum()\nfor n=1:N\n os += \"Pup\",n\nend\nP = MPO(os,sites)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"This code makes an MPO P which is just the sum of a spin-up projection operator acting on every site.","category":"page"},{"location":"examples/Physics.html#Making-a-Custom-state-Definition","page":"Physics (SiteType) System Examples","title":"Making a Custom state Definition","text":"","category":"section"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The function state is used to define states (single-site wavefunctions) that sites can be in. For example, the \"Qubit\" site type includes definitions for the \"0\" and \"1\" states as well as the \"+\" (eigenstate of X operator) state. The \"S=1/2\" site type includes definitions for the \"Up\" and \"Dn\" (down) states.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Say we want to define a new state for the \"Electron\" site type called \"+\", which has the meaning of one electron with its spin in the +X direction. First let's review the existing state definitions:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"ITensors.state(::StateName\"Emp\", ::SiteType\"Electron\") = [1.0, 0, 0, 0]\nITensors.state(::StateName\"Up\", ::SiteType\"Electron\") = [0.0, 1, 0, 0]\nITensors.state(::StateName\"Dn\", ::SiteType\"Electron\") = [0.0, 0, 1, 0]\nITensors.state(::StateName\"UpDn\", ::SiteType\"Electron\") = [0.0, 0, 0, 1]","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"As we can see, the four settings of an \"Electron\" index correspond to the states 0rangle uparrowrangle downarrowrangle uparrowdownarrowrangle.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"So we can define our new state \"+\" as follows:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"ITensors.state(::StateName\"+\", ::SiteType\"Electron\") = [0, 1/sqrt(2), 1/sqrt(2), 0]","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"which makes the state","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"+rangle = frac1sqrt2 uparrowrangle + frac1sqrt2 downarrowrangle","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Having defined this overload of state, if we have an Index of type \"Electron\" we can obtain our new state for it by doing","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"s = siteind(\"Electron\")\nplus = state(\"+\",s)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"We can also use this new state definition in other ITensor features such as the MPS constructor taking an array of state names.","category":"page"},{"location":"examples/Physics.html#Make-a-Custom-Local-Hilbert-Space-/-Physical-Degree-of-Freedom","page":"Physics (SiteType) System Examples","title":"Make a Custom Local Hilbert Space / Physical Degree of Freedom","text":"","category":"section"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"ITensor provides support for a range of common local Hilbert space types, or physical degrees of freedom, such as S=1/2 and S=1 spins; spinless and spinful fermions; and more.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"However, there can be many cases where you need to make custom degrees of freedom. You might be working with an exotic system, such as Z_N parafermions for example, or need to customize other defaults provided by ITensor.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"In ITensor, such a customization is done by overloading functions on specially designated Index tags. Below we give an brief introduction by example of how to make such custom Index site types in ITensor. Other code formulas following this one explain how to build on this example to expand the capabilities of your custom site type such as adding support for quantum number (QN) conservation and defining custom mappings of strings to states.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Throughout we will focus on the example of S=32 spins. These are spins taking the S^z values of +32+12-12-32. So as tensor indices, they are indices of dimension 4.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The key operators we will make for this example are S^z, S^+, and S^-, which are defined as:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"beginaligned\nS^z =\nbeginbmatrix\n32 0 0 0 \n 0 12 0 0 \n 0 0 -12 0 \n 0 0 0 -32\nendbmatrix \n\nS^+ =\nbeginbmatrix\n 0 sqrt3 0 0 \n 0 0 2 0 \n 0 0 0 sqrt3 \n 0 0 0 0 \nendbmatrix \n\nS^- =\nbeginbmatrix\n 0 0 0 0 \n sqrt3 0 0 0 \n 0 2 0 0 \n 0 0 sqrt3 0 \nendbmatrix \nendaligned","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Code Preview","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"First let's see the minimal code needed to define and use this new S=32 site type, then we will discuss what each part of the code is doing.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"using ITensors\n\nITensors.space(::SiteType\"S=3/2\") = 4\n\nITensors.op(::OpName\"Sz\",::SiteType\"S=3/2\") =\n [+3/2 0 0 0\n 0 +1/2 0 0\n 0 0 -1/2 0\n 0 0 0 -3/2]\n\nITensors.op(::OpName\"S+\",::SiteType\"S=3/2\") =\n [0 √3 0 0\n 0 0 2 0\n 0 0 0 √3\n 0 0 0 0]\n\nITensors.op(::OpName\"S-\",::SiteType\"S=3/2\") =\n [0 0 0 0\n √3 0 0 0\n 0 2 0 0\n 0 0 √3 0]\n","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Now let's look at each part of the code above.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The SiteType","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The most important aspect of this code is a special type, known as a SiteType, which is a type made from a string. The string of interest here will be an Index tag. In the code above, the SiteType we are using is","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"SiteType\"S=3/2\"","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"What is the purpose of a SiteType? The answer is that we would like to be able to select different functions to call on an ITensor Index based on what tags it has, but that is not directly possible in Julia or indeed most languages. However, if we can map a tag to a type in the Julia type system, we can create function overloads for that type. ITensor does this for certain functions for you, and we will discuss a few of these functions below. So if the code encounters an Index such as Index(4,\"S=3/2\") it can call these functions which are specialized for indices carrying the \"S=3/2\" tag.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The space Function","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"One of the overloadable SiteType functions is space, whose job is to describe the vector space corresponding to that site type. For our SiteType\"S=3/2\" overload of space, which gets called for any Index carrying the \"S=3/2\" tag, the definition is","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"ITensors.space(::SiteType\"S=3/2\") = 4","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Note that the function name is prepended with ITensors. before space. This prefix makes sure the function is overloading other versions of the space inside the ITensors module.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The only information needed about the vector space of a \"S=3/2\" Index in this example is that it is of dimension four. So the space function returns the integer 4. We will see in more advanced examples that the returned value can instead be an array which specifies not only the dimension of a \"S=3/2\" Index, but also additional subspace structure it has corresponding to quantum numbers.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"After defining this space function, you can just write code like:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"s = siteind(\"S=3/2\")","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"to obtain a single \"S=3/2\" Index, or write code like","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"N = 100\nsites = siteinds(\"S=3/2\",N)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"to obtain an array of N \"S=3/2\" indices. The custom space function will be used to determine the dimension of these indices, and the siteind or siteinds functions provided by ITensor will help with extra things like putting other Index tags that are conventional for site indices.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The op Function","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The op function lets you define custom local operators associated to the physical degrees of freedom of your SiteType. Then for example you can use indices carrying your custom tag with OpSum and the OpSum system will know how to automatically convert names of operators such as \"Sz\" or \"S+\" into ITensors so that it can make an actual MPO.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"In our example above, we defined this function for the case of the \"Sz\" operator as:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"using ITensors # hide\n\nITensors.op(::OpName\"Sz\",::SiteType\"S=3/2\") =\n [+3/2 0 0 0\n 0 +1/2 0 0\n 0 0 -1/2 0\n 0 0 0 -3/2]","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"As you can see, the function is passed two objects: an OpName and a SiteType. The strings \"Sz\" and \"S=3/2\" are also part of the type of these objects, and have the meaning of which operator name we are defining and which site type these operators are defined for.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The body of this overload of ITensors.op constructs and returns a Julia matrix which gives the matrix elements of the operator we are defining.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Once this function is defined, and if you have an Index such as","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"s = Index(4,\"S=3/2\")","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"then, for example, you can get the \"Sz\" operator for this Index and print it out by doing:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Sz = op(\"Sz\",s)\nprintln(Sz)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Again, through the magic of the SiteType system, the ITensor library takes your Index, reads off its tags, notices that one of them is \"S=3/2\", and converts this into the type SiteType\"S=3/2\" in order to call the specialized function ITensors.op defined above.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"You can use the op function yourself with a set of site indices created from the siteinds function like this:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"N = 100\nsites = siteinds(\"S=3/2\",N)\nSz1 = op(\"Sz\",sites[1])\nSp3 = op(\"S+\",sites[3])","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Alternatively, you can write the lines of code above in the style of Sz1 = op(\"Sz\",sites,1).","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"This same op function is used inside of OpSum (formerly called AutoMPO) when it converts its input into an actual MPO. So by defining custom operator names you can pass any of these operator names into OpSum and it will know how to use these operators.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Further Steps","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"See how the built-in site types are defined inside the ITensor library:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"S=1/2 sites - Dimension 2 local Hilbert space. Similar to the \"Qubit\" site type, shares many of the same operator definitions.\nQubit sites - Dimension 2 local Hilbert space. Similar to the \"S=1/2\" site type, shares many of the same operator definitions.\nS=1 sites - Dimension 3 local Hilbert space.\nFermion sites - Dimension 2 local Hilbert space. Spinless fermion site type.\nElectron sites - Dimension 4 local Hilbert space. Spinfull fermion site type.\ntJ sites - Dimension 3 local Hilbert space. Spinfull fermion site type but without a doubly occupied state in the Hilbert space.\nBoson sites - General d-dimensional local Hilbert space. Shares the same operator definitions as the \"Qudit\" site type.\nQudit sites - General d-dimensional local Hilbert space. Generalization of the \"Qubit\" site type, shares the same operator definitions as the Boson site type.","category":"page"},{"location":"examples/Physics.html#Make-a-Custom-Local-Hilbert-Space-with-QNs","page":"Physics (SiteType) System Examples","title":"Make a Custom Local Hilbert Space with QNs","text":"","category":"section"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"In the previous example above, we discussed the basic, minimal code needed to define a custom local Hilbert space, using the example of a S=32 spin Hilbert space. In those examples, the space function defining the vector space of a S=32 spin only provides the dimension of the space. But the Hilbert space of a S=32 spin has additional structure, which is that each of its four subspaces (each of dimension 1) can be labeled by a different S^z quantum number.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"In this code formula we will include this extra quantum information in the definition of the space of a S=32 spin.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Code Preview","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"First let's see the minimal code needed to add the option for including quantum numbers of our S=32 site type, then we will discuss what each part of the code is doing.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"using ITensors\n\nfunction ITensors.space(::SiteType\"S=3/2\";\n conserve_qns=false)\n if conserve_qns\n return [QN(\"Sz\",3)=>1,QN(\"Sz\",1)=>1,\n QN(\"Sz\",-1)=>1,QN(\"Sz\",-3)=>1]\n end\n return 4\nend\n\nITensors.op(::OpName\"Sz\",::SiteType\"S=3/2\") =\n [+3/2 0 0 0\n 0 +1/2 0 0\n 0 0 -1/2 0\n 0 0 0 -3/2]\n\nITensors.op(::OpName\"S+\",::SiteType\"S=3/2\") =\n [0 √3 0 0\n 0 0 2 0\n 0 0 0 √3\n 0 0 0 0]\n\nITensors.op(::OpName\"S-\",::SiteType\"S=3/2\") =\n [0 0 0 0\n √3 0 0 0\n 0 2 0 0\n 0 0 √3 0]\n\n","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Now let's look at each part of the code above.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The space function","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"In the previous code example above, we discussed that the function space tells the ITensor library the basic information about how to construct an Index associated with a special Index tag, in this case the tag \"S=3/2\". As in that code formula, if the user does not request that quantum numbers be included (the case conserve_qns=false) then all that the space function returns is the number 4, indicating that a \"S=3/2\" Index should be of dimension 4.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"But if the conserve_qns keyword argument gets set to true, the space function we defined above returns an array of QN=>Int pairs. (The notation a=>b in Julia constructs a Pair object.) Each pair in the array denotes a subspace. The QN part of each pair says what quantum number the subspace has, and the integer following it indicates the dimension of the subspace.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"After defining the space function this way, you can write code like:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"s = siteind(\"S=3/2\"; conserve_qns=true)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"to obtain a single \"S=3/2\" Index which carries quantum number information. The siteind function built into ITensor relies on your custom space function to ask how to construct a \"S=3/2\" Index but also includes some other Index tags which are conventional for all site indices.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"You can now also call code like:","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"N = 100\nsites = siteinds(\"S=3/2\",N; conserve_qns=true)","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"to obtain an array of N \"S=3/2\" indices which carry quantum numbers.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"The op Function in the Quantum Number Case","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"Note that the op function overloads are exactly the same as for the more basic case of defining an \"S=3/2\" Index type that does not carry quantum numbers. There is no need to upgrade any of the op functions for the QN-conserving case. The reason is that all QN, block-sparse information about an ITensor is deduced from the indices of the tensor, and setting elements of such tensors does not require any other special code.","category":"page"},{"location":"examples/Physics.html","page":"Physics (SiteType) System Examples","title":"Physics (SiteType) System Examples","text":"However, only operators which have a well-defined QN flux–-meaning they always change the quantum number of a state they act on by a well-defined amount–-can be used in practice in the case of QN conservation. Attempting to build an operator, or any ITensor, without a well-defined QN flux out of QN-conserving indices will result in a run time error. An example of an operator that would lead to such an error would be the \"Sx\" spin operator since it alternately increases S^z or decreases S^z depending on the state it acts on, thus it does not have a well-defined QN flux. But it is perfectly fine to define an op overload for the \"Sx\" operator and to make this operator when working with dense, non-QN-conserving ITensors or when S^z is not conserved.","category":"page"},{"location":"RunningOnGPUs.html#Running-on-GPUs","page":"Running on GPUs","title":"Running on GPUs","text":"","category":"section"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"ITensor provides package extensions for running tensor operations on a variety of GPU backends. You can activate a backend by loading the appropriate Julia GPU package alongside ITensors.jl and moving your tensors and/or tensor networks to an available GPU using that package's provided conversion functions.","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"For example, you can load CUDA.jl to perform tensor operations on NVIDIA GPUs or Metal.jl to perform tensor operations on Apple GPUs:","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"using ITensors\n\ni, j, k = Index.((2, 2, 2))\nA = randomITensor(i, j)\nB = randomITensor(j, k)\n\n# Perform tensor operations on CPU\nA * B\n\n###########################################\nusing CUDA # This will trigger the loading of `NDTensorsCUDAExt` in the background\n\n# Move tensors to NVIDIA GPU\nAcu = cu(A)\nBcu = cu(B)\n\n# Perform tensor operations on NVIDIA GPU\nAcu * Bcu\n\n###########################################\nusing Metal # This will trigger the loading of `NDTensorsMetalExt` in the background\n\n# Move tensors to Apple GPU\nAmtl = mtl(A)\nBmtl = mtl(B)\n\n# Perform tensor operations on Apple GPU\nAmtl * Bmtl","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"Note that we highly recommend using these new package extensions as opposed to ITensorGPU.jl, which is ITensor's previous CUDA backend. The package extensions are better integrated into the main library so are more reliable and better supported right now. We plan to deprecate ITensorGPU.jl in the future.","category":"page"},{"location":"RunningOnGPUs.html#GPU-backends","page":"Running on GPUs","title":"GPU backends","text":"","category":"section"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"ITensor currently provides package extensions for the following GPU backends:","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"CUDA.jl (NVIDIA GPUs)\ncuTENSOR.jl (CUDA.jl extension providing accelerated binary tensor contractions) \nMetal.jl (Apple GPUs)\nAMDGPU.jl (AMD GPUs)","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"Our goal is to support all GPU backends which are supported by the JuliaGPU organization.","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"Notice that cuTENSOR.jl is an extension of CUDA.jl that provides new functionality for accelerated binary tensor contractions. If the cuTENSOR.jl library is loaded then ITensors with CuArray data are contracted using cuTENSOR and if the cuTENSOR.jl library is not loaded but CUDA.jl is loaded then binary tensor contractions are mapped to a matrix multiplication and performed using cuBLAS.","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"Some important caveats to keep in mind related to the ITensor GPU backends are:","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"only dense tensor operations are well supported right now. Block sparse operations (which arise when QN conservation is enabled) are under active development and either may not work or may be slower than their CPU counterparts,\ncertain GPU backends do not have native support for certain matrix decompositions like svd, eigen, and qr in which case we will perform those operations on CPU. If your calculation is dominated by those operations, there likely is no advantage to running it on GPU right now. CUDA generally has good support for native matrix decompositions, while Metal and AMD have more limited support right now, and\nsingle precision (Float32) calculations are generally fastest on GPU.","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":"The table below summarizes each backend's current capabilities.","category":"page"},{"location":"RunningOnGPUs.html","page":"Running on GPUs","title":"Running on GPUs","text":" CUDA cuTENSOR ROCm Metal oneAPI\nContractions (dense) ✓ (cuBLAS) ✓ ✓ ✓ N/A\nQR (dense) ✓ (cuSOLVER) ✓ (cuSOLVER) On CPU On CPU N/A\nSVD (dense) ✓ (cuSOLVER) ✓ (cuSOLVER) On CPU On CPU N/A\nEigendecomposition (dense) ✓ (cuSOLVER) ✓ (cuSOLVER) On CPU On CPU N/A\nDouble precision (Float64) ✓ (cuSOLVER) ✓ ✓ N/A N/A\nBlock sparse In progress In progress In progress In progress N/A","category":"page"},{"location":"IncludedSiteTypes.html#SiteTypes-Included-with-ITensor","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"","category":"section"},{"location":"IncludedSiteTypes.html#\"S1/2\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"S=1/2\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Site indices with the \"S=1/2\" site type represent S=12 spins with the states uparrowrangle, downarrowrangle.","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Making a single \"S=1/2\" site or collection of N \"S=1/2\" sites","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"s = siteind(\"S=1/2\")\nsites = siteinds(\"S=1/2\",N)","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"conserve_qns (default: false): conserve total S^z\nconserve_sz (default: conserve_qns): conserve total S^z\nconserve_szparity (default: false): conserve total S^z modulo two\nqnname_sz (default: \"Sz\"): name of total S^z QN\nqnname_szparity (default: \"SzParity\"): name of total S^z modulo two QN","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"For example:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"sites = siteinds(\"S=1/2\",N; conserve_szparity=true, qnname_szparity=\"SzP\")","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Operators associated with \"S=1/2\" sites can be made using the op function, for example","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Sz = op(\"Sz\",s)\nSz4 = op(\"Sz\",sites[4])","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available operators are exactly the same as those for the \"Qubit\" site type. Please see the list of \"Qubit\" operators below.","category":"page"},{"location":"IncludedSiteTypes.html#\"Qubit\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"Qubit\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Site indices with the \"Qubit\" site type represent qubits with the states 0rangle, 1rangle.","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Making a single \"Qubit\" site or collection of N \"Qubit\" sites","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"s = siteind(\"Qubit\")\nsites = siteinds(\"Qubit\",N)","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"conserve_qns (default: false): conserve total qubit parity\nconserve_parity (default: conserve_qns): conserve total qubit parity\nconserve_number (default: false): conserve total qubit number\nqnname_parity (default: \"Parity\"): name of total qubit parity QN\nqnname_number (default: \"Number\"): name of total qubit number QN","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"For example:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"sites = siteinds(\"Qubit\",N; conserve_parity=true)","category":"page"},{"location":"IncludedSiteTypes.html#\"Qubit\"-and-\"S1/2\"-States","page":"SiteTypes Included with ITensor","title":"\"Qubit\" and \"S=1/2\" States","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"The available state names for \"Qubit\" sites are:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"0\" (aliases: \"Z+\", \"Zp\", \"Up\", \"↑\") Qubit in the 0 state\n\"1\" (aliases: \"Z-\", \"Zm\", \"Dn\", \"↓\") Qubit in the 1 state\n\"+\" (aliases: \"X+\", \"Xp\") Qubit in the +rangle state (+1 eigenvector of sigma_x)\n\"+\" (aliases: \"X-\", \"Xm\") Qubit in the -rangle state (-1 eigenvector of sigma_x)\n\"i\" (aliases: \"Y+\", \"Yp\") Qubit in the irangle state (+1 eigenvector of sigma_y)\n\"-i\" (aliases: \"Y-\", \"Ym\") Qubit in the -irangle state (+1 eigenvector of sigma_y)","category":"page"},{"location":"IncludedSiteTypes.html#\"Qubit\"-and-\"S1/2\"-Operators","page":"SiteTypes Included with ITensor","title":"\"Qubit\" and \"S=1/2\" Operators","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Operators or gates associated with \"Qubit\" sites can be made using the op function, for example","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"H = op(\"H\",s)\nH3 = op(\"H\",sites[3])","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Single-qubit operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"X\" (aliases: \"σx\", \"σ1\") Pauli X operator\n\"Y\" (aliases: \"σy\", \"σ2\") Pauli Y operator\n\"iY\" (aliases: \"iσy\", \"iσ2\") Pauli Y operator times i\n\"Z\" (aliases: \"σz\", \"σ3\") Pauli Z operator\n\"√NOT\" (aliases: \"X\")\n\"H\" Hadamard gate\n\"Phase\" (takes optional argument: ϕ=π/2) (aliases: \"P\", \"S\")\n\"π/8\" (aliases: \"T\")\n\"Rx\" (takes argument: θ) Rotation around x axis\n\"Ry\" (takes argument: θ) Rotation around y axis\n\"Rz\" (takes argument: θ) Rotation around z axis\n\"Rn\" (takes arguments: θ, ϕ, λ) (aliases: \"Rn̂\") Rotation about axis n=(θ, ϕ, λ)\n\"Proj0\" (aliases: \"ProjUp\", \"projUp\") Operator 0ranglelangle 0\n\"Proj1\" (aliases: \"ProjDn\", \"projDn\") Operator 1ranglelangle 1","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Spin operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Sz\" (aliases: \"Sᶻ\") Spin z operator S^z = frac12 sigma_z\n\"S+\" (alises: \"S⁺\", \"Splus\") Raising operator S^+ = S^x + iS^y\n\"S-\" (aliases: \"S⁻\", \"Sminus\") Lowering operator S^- = S^x - iS^y\n\"Sx\" (alises: \"Sˣ\") Spin x operator S^x = frac12 sigma_x\n\"iSy\" (aliases: \"iSʸ\") i times spin y operator iS^y = fraci2 sigma_y\n\"Sy\" (aliases: \"Sʸ\") Spin y operator S^y = frac12 sigma_y\n\"S2\" (aliases: \"S²\"`) Square of spin vector operator S^2=vecScdotvecS=frac34 I\n\"ProjUp\" (aliases: \"projUp\", \"Proj0\") Operator ranglelangle \n\"ProjDn\" (aliases: \"projDn\", \"Proj1\") Operator ranglelangle ","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Two-qubit gates:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"CNOT\" (aliases: \"CX\") Controlled NOT gate\n\"CY\" Controlled Y gate\n\"CZ\" Controlled Z gate\n\"CPHASE\" (aliases: \"Cphase\") Controlled Phase gate\n\"CRx\" (aliases: \"CRX\") (takes arguments: θ)\n\"CRy\" (aliases: \"CRY\") (takes arguments: θ)\n\"CRz\" (aliases: \"CRZ\") (takes arguments: θ)\n\"CRn\" (aliases: \"CRn̂\") (takes arguments: θ, ϕ, λ)\n\"SWAP\" (aliases: \"Swap\")\n\"√SWAP\" (aliases: \"√Swap\")\n\"iSWAP\" (aliases: \"iSwap\")\n\"√iSWAP\" (aliases: \"√iSwap\")\n\"Rxx\" (aliases: \"RXX\") (takes arguments: ϕ) Ising (XX) coupling gate\n\"Ryy\" (aliases: \"RYY\") (takes arguments: ϕ) Ising (YY) coupling gate\n\"Rzz\" (aliases: \"RZZ\") (takes arguments: ϕ) Ising (ZZ) coupling gate","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Three-qubit gates:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Toffoli\" (aliases \"CCNOT\", \"CCX\", \"TOFF\")\n\"Fredkin\" (aliases \"CSWAP\", \"CSwap\", \"CS\")","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Four-qubit gates:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"CCCNOT\"","category":"page"},{"location":"IncludedSiteTypes.html#\"S1\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"S=1\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Site indices with the \"S=1\" site type represent S=1 spins with the states uparrowrangle, 0rangle, downarrowrangle.","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Making a single \"S=1\" site or collection of N \"S=1\" sites","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"s = siteind(\"S=1\")\nsites = siteinds(\"S=1\",N)","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"conserve_qns (default: false): conserve total S^z\nconserve_sz (default: conserve_qns): conserve total S^z\nqnname_sz (default: \"Sz\"): name of total S^z QN","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"For example:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"sites = siteinds(\"S=1\",N; conserve_sz=true, qnname_sz=\"TotalSz\")","category":"page"},{"location":"IncludedSiteTypes.html#\"S1\"-States","page":"SiteTypes Included with ITensor","title":"\"S=1\" States","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"The available state names for \"S=1\" sites are:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Up\" (aliases: \"Z+\", \"↑\") spin in the up state\n\"Z0\" (aliases: \"0\") spin in the Sz=0 state\n\"Dn\" (aliases: \"Z-\", \"↓\") spin in the Sz=0 state","category":"page"},{"location":"IncludedSiteTypes.html#\"S1\"-Operators","page":"SiteTypes Included with ITensor","title":"\"S=1\" Operators","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Operators associated with \"S=1\" sites can be made using the op function, for example","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Sz = op(\"Sz\",s)\nSz4 = op(\"Sz\",sites[4])","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Spin operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Sz\" (aliases: \"Sᶻ\")\n\"Sz2\" Square of S^z operator\n\"S+\" (alises: \"S⁺\", \"Splus\")\n\"S-\" (aliases: \"S⁻\", \"Sminus\")\n\"Sx\" (alises: \"Sˣ\")\n\"Sx2\" Square of S^x operator\n\"iSy\" (aliases: \"iSʸ\")\n\"Sy\" (aliases: \"Sʸ\")\n\"Sy2\" Square of S^y operator\n\"S2\" (aliases: \"S²\"`)","category":"page"},{"location":"IncludedSiteTypes.html#\"Boson\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"Boson\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"The \"Boson\" site type is an alias for the \"Qudit\" site type. Please see more information about \"Qudit\" below:","category":"page"},{"location":"IncludedSiteTypes.html#\"Qudit\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"Qudit\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Making a single \"Qudit\" site or collection of N \"Qudit\" sites","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"s = siteind(\"Qudit\")\nsites = siteinds(\"Qudit\",N)","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"dim (default: 2): dimension of the index (number of qudit or boson values)\nconserve_qns (default: false): conserve total qudit or boson number\nconserve_number (default: conserve_qns): conserve total qudit or boson number\nqnname_number (default: \"Number\"): name of total qudit or boson number QN","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"For example:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"sites = siteinds(\"Qudit\",N; conserve_number=true)","category":"page"},{"location":"IncludedSiteTypes.html#\"Qudit\"-and-\"Boson\"-Operators","page":"SiteTypes Included with ITensor","title":"\"Qudit\" and \"Boson\" Operators","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Operators associated with \"Qudit\" sites can be made using the op function, for example","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"A = op(\"A\",s)\nA4 = op(\"A\",sites[4])","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Single-qudit operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"A\" (aliases: \"a\")\n\"Adag\" (aliases: \"adag\", \"a†\")\n\"N\" (aliases: \"n\")","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Two-qudit operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"ab\"\n\"a†b\"\n\"ab†\"\n\"a†b†\"","category":"page"},{"location":"IncludedSiteTypes.html#\"Fermion\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"Fermion\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Site indices with the \"Fermion\" SiteType represent spinless fermion sites with the states 0rangle, 1rangle, corresponding to zero fermions or one fermion.","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Making a single \"Fermion\" site or collection of N \"Fermion\" sites","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"s = siteind(\"Fermion\")\nsites = siteinds(\"Fermion\",N)","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"conserve_qns (default: false): conserve total number of fermions\nconserve_nf (default: conserve_qns): conserve total number of fermions\nconserve_nfparity (default: conserve_qns): conserve total fermion number parity\nqnname_nf (default: \"Nf\"): name of total fermion number QN\nqnname_nfparity (default: \"NfParity\"): name of total fermion number parity QN","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"For example:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"sites = siteinds(\"Fermion\",N; conserve_nfparity=true)","category":"page"},{"location":"IncludedSiteTypes.html#\"Fermion\"-States","page":"SiteTypes Included with ITensor","title":"\"Fermion\" States","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"The available state names for \"Fermion\" sites are:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"0\" (aliases: \"Emp\") unoccupied fermion site\n\"1\" (aliases: \"Occ\") occupied fermion site","category":"page"},{"location":"IncludedSiteTypes.html#\"Fermion\"-Operators","page":"SiteTypes Included with ITensor","title":"\"Fermion\" Operators","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Operators associated with \"Fermion\" sites can be made using the op function, for example","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"C = op(\"C\",s)\nC4 = op(\"C\",sites[4])","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Single-fermion operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"N\" (aliases: \"n\") Density operator\n\"C\" (aliases: \"c\") Fermion annihilation operator\n\"Cdag\" (aliases: \"cdag\", \"c†\") Fermion creation operator\n\"F\" Jordan-Wigner string operator","category":"page"},{"location":"IncludedSiteTypes.html#\"Electron\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"Electron\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"The states of site indices with the \"Electron\" SiteType correspond to 0rangle, uparrowrangle, downarrowrangle, uparrowdownarrowrangle.","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Making a single \"Electron\" site or collection of N \"Electron\" sites","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"s = siteind(\"Electron\")\nsites = siteinds(\"Electron\",N)","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"conserve_qns (default: false): conserve total number of electrons\nconserve_sz (default: conserve_qns): conserve total S^z\nconserve_nf (default: conserve_qns): conserve total number of electrons\nconserve_nfparity (default: conserve_qns): conserve total electron number parity\nqnname_sz (default: \"Sz\"): name of total S^z QN\nqnname_nf (default: \"Nf\"): name of total electron number QN\nqnname_nfparity (default: \"NfParity\"): name of total electron number parity QN","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"For example:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"sites = siteinds(\"Electron\",N; conserve_nfparity=true)","category":"page"},{"location":"IncludedSiteTypes.html#\"Electron\"-States","page":"SiteTypes Included with ITensor","title":"\"Electron\" States","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"The available state names for \"Electron\" sites are:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Emp\" (aliases: \"0\") unoccupied electron site\n\"Up\" (aliases: \"↑\") electron site occupied with one up electron\n\"Dn\" (aliases: \"↓\") electron site occupied with one down electron\n\"UpDn\" (aliases: \"↑↓\") electron site occupied with two electrons (one up, one down)","category":"page"},{"location":"IncludedSiteTypes.html#\"Electron\"-Operators","page":"SiteTypes Included with ITensor","title":"\"Electron\" Operators","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Operators associated with \"Electron\" sites can be made using the op function, for example","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Cup = op(\"Cup\",s)\nCup4 = op(\"Cup\",sites[4])","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Single-fermion operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Ntot\" (aliases: \"ntot\") Total density operator\n\"Nup\" (aliases: \"n↑\") Up density operator\n\"Ndn\" (aliases: \"n↓\") Down density operator\n\"Cup\" (aliases: \"c↑\") Up-spin annihilation operator\n\"Cdn\" (aliases: \"c↓\") Down-spin annihilation operator\n\"Cdagup\" (aliases: \"c†↑\") Up-spin creation operator\n\"Cdagdn\" (aliases: \"c†↓\") Down-spin creation operator\n\"Sz\" (aliases: \"Sᶻ\")\n\"Sx\" (aliases: \"Sˣ\")\n\"S+\" (aliases: \"Sp\", \"S⁺\",\"Splus\")\n\"S-\" (aliases: \"Sm\", \"S⁻\", \"Sminus\")\n\"F\" Jordan-Wigner string operator\n\"Fup\" (aliases: \"F↑\") Up-spin Jordan-Wigner string operator\n\"Fdn\" (aliases: \"F↓\") Down-spin Jordan-Wigner string operator","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Non-fermionic single particle operators (these do not have Jordan-Wigner string attached, so will commute within systems such as OpSum or the apply function):","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Aup\" (aliases: \"a↑\") Up-spin annihilation operator\n\"Adn\" (aliases: \"a↓\") Down-spin annihilation operator\n\"Adagup\" (aliases: \"a†↑\") Up-spin creation operator\n\"Adagdn\" (aliases: \"a†↓\") Down-spin creation operator","category":"page"},{"location":"IncludedSiteTypes.html#\"tJ\"-SiteType","page":"SiteTypes Included with ITensor","title":"\"tJ\" SiteType","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"tJ\" sites are similar to electron sites, but cannot be doubly occupied The states of site indices with the \"tJ\" SiteType correspond to 0rangle, uparrowrangle, downarrowrangle.","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Making a single \"tJ\" site or collection of N \"tJ\" sites","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"s = siteind(\"tJ\")\nsites = siteinds(\"tJ\",N)","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Available keyword arguments for enabling and customizing quantum numbers (QN) subspaces:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"conserve_qns (default: false): conserve total number of fermions\nconserve_nf (default: conserve_qns): conserve total number of fermions\nconserve_nfparity (default: conserve_qns): conserve total fermion number parity\nqnname_nf (default: \"Nf\"): name of total fermion number QN\nqnname_nfparity (default: \"NfParity\"): name of total fermion number parity QN","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"For example:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"sites = siteinds(\"tJ\",N; conserve_nfparity=true)","category":"page"},{"location":"IncludedSiteTypes.html#\"tJ\"-States","page":"SiteTypes Included with ITensor","title":"\"tJ\" States","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"The available state names for \"tJ\" sites are:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Emp\" (aliases: \"0\") unoccupied site\n\"Up\" (aliases: \"↑\") site occupied with one up electron\n\"Dn\" (aliases: \"↓\") site occupied with one down electron","category":"page"},{"location":"IncludedSiteTypes.html#\"tJ\"-Operators","page":"SiteTypes Included with ITensor","title":"\"tJ\" Operators","text":"","category":"section"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Operators associated with \"tJ\" sites can be made using the op function, for example","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Cup = op(\"Cup\",s)\nCup4 = op(\"Cup\",sites[4])","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Single-fermion operators:","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Ntot\" (aliases: \"ntot\") Total density operator\n\"Nup\" (aliases: \"n↑\") Up density operator\n\"Ndn\" (aliases: \"n↓\") Down density operator\n\"Cup\" (aliases: \"c↑\") Up-spin annihilation operator\n\"Cdn\" (aliases: \"c↓\") Down-spin annihilation operator\n\"Cdagup\" (aliases: \"c†↑\") Up-spin creation operator\n\"Cdagdn\" (aliases: \"c†↓\") Down-spin creation operator\n\"Sz\" (aliases: \"Sᶻ\")\n\"Sx\" (aliases: \"Sˣ\")\n\"S+\" (aliases: \"Sp\", \"S⁺\",\"Splus\")\n\"S-\" (aliases: \"Sm\", \"S⁻\", \"Sminus\")\n\"F\" Jordan-Wigner string operator\n\"Fup\" (aliases: \"F↑\") Up-spin Jordan-Wigner string operator\n\"Fdn\" (aliases: \"F↓\") Down-spin Jordan-Wigner string operator","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"Non-fermionic single particle operators (these do not have Jordan-Wigner string attached, so will commute within systems such as OpSum or the apply function):","category":"page"},{"location":"IncludedSiteTypes.html","page":"SiteTypes Included with ITensor","title":"SiteTypes Included with ITensor","text":"\"Aup\" (aliases: \"a↑\") Up-spin annihilation operator\n\"Adn\" (aliases: \"a↓\") Down-spin annihilation operator\n\"Adagup\" (aliases: \"a†↑\") Up-spin creation operator\n\"Adagdn\" (aliases: \"a†↓\") Down-spin creation operator","category":"page"},{"location":"faq/HPC.html#High-Performance-Computing-(HPC)-Frequently-Asked-Questions","page":"High-Performance Computing FAQs","title":"High Performance Computing (HPC) Frequently Asked Questions","text":"","category":"section"},{"location":"faq/HPC.html#My-code-is-using-a-lot-of-RAM-what-can-I-do-about-this?","page":"High-Performance Computing FAQs","title":"My code is using a lot of RAM - what can I do about this?","text":"","category":"section"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"Tensor network algorithms can often use a large amount of RAM. On top of this essential fact, the Julia programming languge is \"garbage collected\" which means that unused memory isn't given back to the operating system right away, but only when the Julia runtime dynamically reclaims it. When your code allocates memory very rapidly, this can lead to high memory usage overall.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"Fortunately there are various steps you can take to keep the memory usage of your code under control.","category":"page"},{"location":"faq/HPC.html#.-Avoid-Repeatedly-Allocating,-Especially-in-Fast-or-\"Hot\"-Loops","page":"High-Performance Computing FAQs","title":"1. Avoid Repeatedly Allocating, Especially in Fast or \"Hot\" Loops","text":"","category":"section"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"More memory gets used whenever your code \"allocates\", which happens most commonly when you use dynamic storage types like Vector and Matrix. If you have a code pattern where you allocate or resize an array or vector inside a 'hot' loop, meaning a loop that iterates quickly very many times, the memory from the previous allocations may pile up very quickly before the next garbage collector run.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"To avoid this, allocate the array once before the loop begins if possible, then overwrite its contents during each iteration. More generally, try as much as possible to estimate the sizes of dynamic resources ahead of time. Or do one allocation that creates a large enough \"workspace\" that dynamic algorithms can reuse part of without reallocating the whole workspace (i.e. making a large array once then using portions of it when smaller arrays are needed).","category":"page"},{"location":"faq/HPC.html#.-Use-the-heap-size-hint-Flag","page":"High-Performance Computing FAQs","title":"2. Use the --heap-size-hint Flag","text":"","category":"section"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"A simple step you can take to help with overall memory usage is to pass the --heap-size-hint flag to the Julia program when you start it. For example, you can call Julia as:","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"julia --heap-size-hint=60G","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"When you pass this heap size, Julia will try to keep the memory usage at or below this value if possible.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"In cases where this does not work, your code simply may be allocating too much memory. Be sure not to allocate over and over again inside of \"hot\" loops which execute many times.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"Another possibility is that you are simply working with a tensor network with large bond dimensions, which may fundamentally use a lot of memory. In those cases, you can try to use features such as \"write to disk mode\" of the ITensor DMRG code or other related techniques. (See the write_when_maxdim_exceeds keyword of the ITensor dmrg function.)","category":"page"},{"location":"faq/HPC.html#.-In-Rare-Case,-Force-a-Garbage-Collection-Run","page":"High-Performance Computing FAQs","title":"3. In Rare Case, Force a Garbage Collection Run","text":"","category":"section"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"In some rare cases, such as when your code cannot be optimized to avoid any more allocations or when the --heap-size-hint provided above is not affecting the behavior of the Julia garbage collector, you can force the garbage collector (GC) to run at a specific point in your code by calling:","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"GC.gc()","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"Alternatively, you can call GC.gc(true) to force a \"full run\" rather than just collecting a more 'young' subset of previous allocations.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"While this approach works well to reduce memory usage, it can have the unfortunate downside of slowing down your code each time the garbage collector runs, which can be especially harmful to multithreaded or parallel algorithms. Therefore, if this approach must be used try calling GC.gc() as infrequently as possible and ideally only in the outermost functions and loops of your code (highest levels of your code).","category":"page"},{"location":"faq/HPC.html#Can-Julia-Be-Used-to-Perform-Parallel,-Distributed-Calculations-on-Large-Clusters?","page":"High-Performance Computing FAQs","title":"Can Julia Be Used to Perform Parallel, Distributed Calculations on Large Clusters?","text":"","category":"section"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"Yes. The Julia ecosystem offers multiple approaches to parallel computing across multiple machines including on large HPC clusters and including GPU resources.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"For an overall view of some of these options, the Julia on HPC Clusters website is a good resource.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"Some of the leading approaches to parallelism in Julia are:","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"MPI, through the MPI.jl package. Has the advantage of optionally using an MPI backend that is optimized for a particular cluster and possibly using fast interconnects like Infiniband.\nDagger, a framework for parallel computing across all kinds of resources, like CPUs and GPUs, and across multiple threads and multiple servers.\nDistributed. Part of the base Julia library, giving tools to perform calculations distributed across multiple machines.","category":"page"},{"location":"faq/HPC.html#Does-My-Cluster-Admin-Have-to-Install-Julia-for-Me?-What-are-the-Best-Practices-for-Installing-Julia-on-Clusters?","page":"High-Performance Computing FAQs","title":"Does My Cluster Admin Have to Install Julia for Me? What are the Best Practices for Installing Julia on Clusters?","text":"","category":"section"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"The most common approach to installing and using Julia on clusters is for users to install their own Julia binary and dependencies, which is quite easy to do. However, for certain libraries like MPI.jl, there may be MPI backends that are preferred by the cluster administrator. Fortunately, it is possible for admins to set global defaults for such backends and other library preferences.","category":"page"},{"location":"faq/HPC.html","page":"High-Performance Computing FAQs","title":"High-Performance Computing FAQs","text":"For more information on best practices for installing Julia on clusters, see the Julia on HPC Clusters website.","category":"page"},{"location":"faq/JuliaPkg.html#Julia-Package-Manager-Frequently-Asked-Questions","page":"Julia Package Manager FAQs","title":"Julia Package Manager Frequently Asked Questions","text":"","category":"section"},{"location":"faq/JuliaPkg.html#What-if-I-can't-upgrade-ITensors.jl-to-the-latest-version?","page":"Julia Package Manager FAQs","title":"What if I can't upgrade ITensors.jl to the latest version?","text":"","category":"section"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"Sometimes you may find that doing ] update ITensors or equivalently doing ] up ITensors within Julia package manager mode doesn't result in the ITensors package actually being upgraded. You may see that the current version you have remains stuck to a version that is lower than the latest one which you can check here.","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"What is most likely going on is that you have other packages installed which are blocking ITensors from being updated.","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"To get more information into which packages may be doing this, and what versions they are requiring, you can do the following. First look up the latest version of ITensors.jl. Let's say for this example that it is v0.3.0.","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"Next, input the following command while in package manager mode:","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"julia> ]\npkg> add ITensors@v0.3.0","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"If the package manager cannot update to this version, it will list all of the other packages that are blocking this from happening and give information about why. To go into a little more depth, each package has a compatibility or \"compat\" entry in its Project.toml file which says which versions of the ITensors package it is compatible with. If these versions do not include the latest one, perhaps because the package has not been updated, then it can block the ITensors package from being updated on your system.","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"Generally the solution is to just update each of these packages, then try again to update ITensors. If that does not work, then check the following","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"Are any of the blocking packages in \"dev mode\" meaning you called dev PackageName on them in the past? Try doing free PackageName if so to bring them out of dev mode.\nAre any of the blocking packages unregistered packages that were installed through a GitHub repo link? If so, you may need to do something like add https://github.com/Org/PackageName#main to force update that package to the latest code available on its main branch.","category":"page"},{"location":"faq/JuliaPkg.html","page":"Julia Package Manager FAQs","title":"Julia Package Manager FAQs","text":"If you still can't get the ITensors package update, feel free to post a question or contact us for help.","category":"page"},{"location":"faq/QN.html#Quantum-Number-Frequently-Asked-Questions","page":"Quantum Number (QN) FAQs","title":"Quantum Number Frequently Asked Questions","text":"","category":"section"},{"location":"faq/QN.html#Can-I-mix-different-types-of-quantum-numbers-within-the-same-system?","page":"Quantum Number (QN) FAQs","title":"Can I mix different types of quantum numbers within the same system?","text":"","category":"section"},{"location":"faq/QN.html","page":"Quantum Number (QN) FAQs","title":"Quantum Number (QN) FAQs","text":"Yes, you can freely mix quantum numbers (QNs) of different types. For example, you can make the sites of your systems alternate between sites carrying spin \"Sz\" QNs and fermion sites carrying particle number \"Nf\" QNs. The QNs will not mix with each other and will separately be conserved to the original value you set for your initial wavefunction.","category":"page"},{"location":"faq/QN.html#How-can-I-separately-conserve-QNs-which-have-the-same-name?","page":"Quantum Number (QN) FAQs","title":"How can I separately conserve QNs which have the same name?","text":"","category":"section"},{"location":"faq/QN.html","page":"Quantum Number (QN) FAQs","title":"Quantum Number (QN) FAQs","text":"If you have two physically distinct types of sites, such as \"Qudit\" sites, but which carry identically named QNs called \"Number\", and you want the qudit number to be separately conserved within each type of site, you must make the QN names different for the two types of sites.","category":"page"},{"location":"faq/QN.html","page":"Quantum Number (QN) FAQs","title":"Quantum Number (QN) FAQs","text":"For example, the following line of code will make an array of site indices with the qudit number QN having the name \"Number_odd\" on odd sites and \"Number_even\" on even sites:","category":"page"},{"location":"faq/QN.html","page":"Quantum Number (QN) FAQs","title":"Quantum Number (QN) FAQs","text":"sites = [isodd(n) ? siteind(\"Qudit\", n; dim=10, conserve_qns=true, qnname_number=\"Number_odd\")\n : siteind(\"Qudit\", n; dim=2, conserve_qns=true, qnname_number=\"Number_even\")\n for n in 1:2*L]","category":"page"},{"location":"faq/QN.html","page":"Quantum Number (QN) FAQs","title":"Quantum Number (QN) FAQs","text":"(You may have to collapse the above code into a single line for it to run properly.)","category":"page"},{"location":"faq/RelationshipToOtherLibraries.html#Relationship-of-ITensor-to-other-tensor-libraries","page":"Relationship of ITensor to other tensor libraries FAQs","title":"Relationship of ITensor to other tensor libraries","text":"","category":"section"},{"location":"faq/RelationshipToOtherLibraries.html","page":"Relationship of ITensor to other tensor libraries FAQs","title":"Relationship of ITensor to other tensor libraries FAQs","text":"Here we will describe the relationship of ITensor to more traditional Julia Arrays or deep learning libraries like TensorFlow and PyTorch. There are a few things that distinguish ITensor from those approaches:","category":"page"},{"location":"faq/RelationshipToOtherLibraries.html","page":"Relationship of ITensor to other tensor libraries FAQs","title":"Relationship of ITensor to other tensor libraries FAQs","text":"ITensors have dimensions with labels that get passed around, which makes it simple to perform certain operations like contraction, addition, and tensor decompositions with a high level interface, independent of memory layout. This is along the same lines as Julia packages like NamedDims.jl and AxisArrays.jl and libraries in Python like xarray, however I would argue that the ITensor approach is a little more sophisticated (the dimensions have more metadata which makes them easier to manipulate for different situations, random ids to help avoid name clashes, etc.). This design was inspired by the needs of tensor network algorithms, where there are many tensor dimensions in the computation (of which many of them are dynamically created during the calculation), but would be helpful for writing other algorithms too.\nThe ITensor type has a dynamic high level interface, where the type itself is mutable and the data can be swapped out. This allows for conveniently allocating the data of an ITensor on the fly \"as needed\", which makes for a nicer, more flexible interface (like initializing an empty ITensor before a loop, and filling it with the correct data type when the first value is set), at the expense of a small overhead for accessing data in the ITensor. We have found this tradeoff is worth it, since we expect ITensors to be used for medium to large scale calculations where operations on the tensors like contraction, addition, and tensor decomposition dominate the cost of the calculation, and code can be designed with function barriers to speed up operations when data is being accessed repeatedly.\nAnother feature that ITensor has that goes beyond what is available in standard Julia, TensorFlow, and PyTorch is tensors which are symmetric under a group action. The physical interpretation of these tensors are ones that have a conserved quantity (like a quantum state with a conserved number of particles), so that feature is more physics-oriented, but could have applications in other areas like machine learning as well. In practice, these tensors are block sparse, and have extra metadata on the dimensions labeling representations of the group.\nBased on the features above, the ITensor library provides high level implementations of tensor network algorithms (algebraic operations of very high dimensional tensors, such as addition, multiplication, and finding dominant eigenvectors). In general these algorithms can (and have been) written on top of other libraries like standard Julia Arrays/AD, PyTorch, or TensorFlow, but they might have various downsides (a less convenient interface for dealing with tensor operations, no support for the types of symmetric tensors we often need, limited support for tensors with complex numbers in the case of libraries like PyTorch, though perhaps that has improved since I last checked, etc.).","category":"page"},{"location":"faq/RelationshipToOtherLibraries.html","page":"Relationship of ITensor to other tensor libraries FAQs","title":"Relationship of ITensor to other tensor libraries FAQs","text":"Although ITensor has primarily focused on quantum physics and quantum computing applications, there is work using ITensor for machine learning applications (so far focused on applications of tensor networks to machine learning, so no neural network calculations yet as far as I know). In general, these different libraries (ITensor, Flux, PyTorch, TensorFlow) are biased towards their specific methods and application areas that they are used for the most: ITensor is more biased towards tensor network calculations and quantum physics/quantum computing applications, based on the available features and interface, while PyTorch and TensorFlow are more biased towards neural network calculations. However, our goal would be to provide more features to ITensor that would make it useful for neural network applications as well, such as better support for slicing operations.","category":"page"},{"location":"AdvancedUsageGuide.html#advanced_usage_guide","page":"Advanced Usage Guide","title":"Advanced ITensor Usage Guide","text":"","category":"section"},{"location":"AdvancedUsageGuide.html#Installing-and-updating-ITensors.jl","page":"Advanced Usage Guide","title":"Installing and updating ITensors.jl","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"The ITensors package can be installed with the Julia package manager. Assuming you have already downloaded Julia, which you can get here, from the Julia REPL, type ] to enter the Pkg REPL mode and run:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"$ julia","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> ]\n\npkg> add ITensors","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Or, equivalently, via the Pkg API:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> import Pkg; Pkg.add(\"ITensors\")","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"We recommend using ITensors.jl with Intel MKL in order to get the best possible performance. If you have not done so already, you can replace the current BLAS and LAPACK implementation used by Julia with MKL by using the MKL.jl package. Please follow the instructions here.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"To use the latest registered (stable) version of ITensors.jl, use update ITensors in Pkg mode or import Pkg; Pkg.update(\"ITensors\"). We will commonly release new patch versions (such as updating from v0.1.12 to v0.1.13) with bug fixes and improvements. However, make sure to double check before updating between minor versions (such as from v0.1.41 to v0.2.0) because new minor releases may be breaking.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Remember that if you are compiling system images of ITensors.jl, such as with the ITensors.compile() command, you will need to rerurn this command to compile the new version of ITensor after an update.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"To try the \"development branch\" of ITensors.jl (for example, if there is a feature or fix we added that hasn't been released yet), you can do add ITensors#main. You can switch back to the latest released version with add ITensors. Using the development/main branch is generally not encouraged unless you know what you are doing.","category":"page"},{"location":"AdvancedUsageGuide.html#Using-ITensors.jl-in-the-REPL","page":"Advanced Usage Guide","title":"Using ITensors.jl in the REPL","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"There are many ways you can write code based on ITensors.jl, ranging from using it in the REPL to writing a small script to making a package that depends on it.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"For example, you can just start the REPL from your command line like:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"$ julia","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"assuming you have an available version of Julia with the ITensors.jl package installed. Then just type:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using ITensors","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"and start typing ITensor commands. For example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> i = Index(2, \"i\")\n(dim=2|id=355|\"i\")\n\njulia> A = randomITensor(i, i')\nITensor ord=2 (dim=2|id=355|\"i\") (dim=2|id=355|\"i\")'\nNDTensors.Dense{Float64,Array{Float64,1}}\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=355|\"i\")\nDim 2: (dim=2|id=355|\"i\")'\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 1.2320011464276275 1.8504245734277216\n 1.0763652402177477 0.030353720156277037\n\njulia> (A*dag(A))[]\n3.9627443142240617","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Note that there are some \"gotchas\" with working in the REPL like this. Technically, all commands in the REPL are in the \"global scope\". The global scope might not work as you would expect, for example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> for _ in 1:3\n A *= 2\n end\nERROR: UndefVarError: A not defined\nStacktrace:\n [1] top-level scope at ./REPL[12]:2\n [2] eval(::Module, ::Any) at ./boot.jl:331\n [3] eval_user_input(::Any, ::REPL.REPLBackend) at /home/mfishman/software/julia-1.4.0/share/julia/stdlib/v1.4/REPL/src/REPL.jl:86\n [4] run_backend(::REPL.REPLBackend) at /home/mfishman/.julia/packages/Revise/AMRie/src/Revise.jl:1023\n [5] top-level scope at none:0","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"since the A inside the for-loop introduces a new local variable. Some alternatives are to wrap that part of the code in a let-block or a function:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> function f(A)\n for _ in 1:3\n A *= 2\n end\n A\n end\nf (generic function with 1 method)\n\njulia> A = f(A)\nITensor ord=2 (dim=2|id=355|\"i\") (dim=2|id=355|\"i\")'\nNDTensors.Dense{Float64,Array{Float64,1}}\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=355|\"i\")\nDim 2: (dim=2|id=355|\"i\")'\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 9.85600917142102 14.803396587421773\n 8.610921921741982 0.2428297612502163","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"In this particular case, you can alternatively modify the ITensor in-place:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> for _ in 1:3\n A ./= 2\n end\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=355|\"i\")\nDim 2: (dim=2|id=355|\"i\")'\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 1.2320011464276275 1.8504245734277216\n 1.0763652402177477 0.030353720156277037","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"A common place you might accidentally come across this is when you are creating a Hamiltonian with OpSum:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> N = 4;\n\njulia> sites = siteinds(\"S=1/2\",N);\n\njulia> os = OpSum();\n\njulia> for j=1:N-1\n os += \"Sz\", j, \"Sz\", j+1\n end\nERROR: UndefVarError: os not defined\nStacktrace:\n [1] top-level scope at ./REPL[16]:2\n [2] eval(::Module, ::Any) at ./boot.jl:331\n [3] eval_user_input(::Any, ::REPL.REPLBackend) at /home/mfishman/software/julia-1.4.0/share/julia/stdlib/v1.4/REPL/src/REPL.jl:86\n [4] run_backend(::REPL.REPLBackend) at /home/mfishman/.julia/packages/Revise/AMRie/src/Revise.jl:1023\n [5] top-level scope at none:0","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"In this case, you can use os .+= (\"Sz\", j, \"Sz\", j+1), add!(os, \"Sz\", j, \"Sz\", j+1), or wrap your code in a let-block or function.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Take a look at Julia's documentation here for rules on scoping. Also note that this behavior is particular to Julia v1.4 and below, and is expected to change in v1.5.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Note that the REPL is very useful for prototyping code quickly, but working directly in the REPL and outside of functions can cause sub-optimal performance. See Julia's performance tips for more information.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"We recommend the package OhMyREPL which adds syntax highlighting to the Julia REPL.","category":"page"},{"location":"AdvancedUsageGuide.html#Finding-documentation-interactively","page":"Advanced Usage Guide","title":"Finding documentation interactively","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Julia provides many tools for searching for documentation interactively at the REPL. Say that you want to learn more about how to use an ITensor from the command line. You can start by typing ? followed by ITensor:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using ITensors\n\njulia> ?ITensor\nsearch: ITensor ITensors itensor emptyITensor randomITensor\n\n An ITensor is a tensor whose interface is independent of its\n memory layout. Therefore it is not necessary to know the ordering\n of an ITensor's indices, only which indices an ITensor has.\n Operations like contraction and addition of ITensors automatically\n handle any memory permutations.\n\n Examples\n ≡≡≡≡≡≡≡≡≡≡\n\n julia> i = Index(2, \"i\")\n (dim=2|id=287|\"i\")\n\n julia> A = randomITensor(i', i)\n ITensor ord=2 (dim=2|id=287|\"i\")' (dim=2|id=287|\"i\")\n NDTensors.Dense{Float64,Array{Float64,1}}\n\n julia> @show A;\n A = ITensor ord=2\n Dim 1: (dim=2|id=287|\"i\")'\n Dim 2: (dim=2|id=287|\"i\")\n NDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 0.28358594718392427 1.4342219756446355\n 1.6620103556283987 -0.40952231269251566\n\n julia> @show inds(A);\n inds(A) = IndexSet{2} (dim=2|id=287|\"i\")' (dim=2|id=287|\"i\")\n[...]","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"(the specific output may be different for different versions of ITensors.jl as we update the docs). You can use the help prompt (which you get by typing ? at the REPL) to print out documentation for types and methods.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Another way to get information about types is with the function fieldnames:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> fieldnames(ITensor)\n(:store, :inds)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"which shows the fields of a type. Note that in general the specific names of the fields and structures of types may change (we consider those to be internal details), however we often make functions to access the fields of a type that have the same name as the field, so it is a good place to get started. For example, you can access the storage and indices of an ITensor A with the functions store(A) and inds(A).","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Another helpful function is apropos, which search through all documentation for a string (ignoring the case) and prints a list of all types and methods with documentation that contain the string. For example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> apropos(\"IndexSet\")\nITensors.IndexSet\nITensors.push\nITensors.insertat\nITensors.getfirst\nITensors.commoninds\nITensors.pushfirst\nNDTensors.mindim\n[...]","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This can often return too much information. A helpful way to narrow down the search is with regular expressions, for example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> apropos(r\"ITensor.*IndexSet\")\nITensors.block\nITensors.hasinds\nITensors.ITensor\nNDTensors.inds","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"where the notation r\"...\" is Julia notation for making a string that will be interpreted as a regular expression. Here, we are searching for any documentation that contains the string \"ITensor\" followed at some point by \"IndexSet\". The notation .* is regular expression notation for matching any number of any type of character.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Based on the apropos function, we can make some helper functions that may be useful. For example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"using ITensors\n\nfunction finddocs(s)\n io = IOBuffer()\n apropos(io, s)\n v = chomp(String(take!(io)))\n return split(v, \"\\n\")\nend\n\nfunction finddocs(s...)\n intersect(finddocs.(s)...)\nend\n\nfound_methods = finddocs(\"indices\", \"set difference\")\ndisplay(found_methods)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"returns:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"3-element Array{SubString{String},1}:\n \"ITensors.noncommoninds\"\n \"Base.setdiff\"\n \"ITensors.uniqueinds\"","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"which are the functions that have docs that contain the strings \"indices\" and \"set difference\". We can print the docs for uniqueinds to find:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"help?> uniqueinds\nsearch: uniqueinds unique_siteinds uniqueind uniqueindex\n\n uniqueinds(A, B; kwargs...)\n uniqueinds(::Order{N}, A, B; kwargs...)\n\n\n Return an IndexSet with indices that are unique to the set of\n indices of A and not in B (the set difference).\n\n Optionally, specify the desired number of indices as Order(N),\n which adds a check and can be a bit more efficient.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"We can also filter the results to only specify functions from certain modules, for example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> filter(x -> startswith(x, \"ITensors\"), finddocs(\"indices\", \"set difference\"))\n2-element Array{SubString{String},1}:\n \"ITensors.noncommoninds\"\n \"ITensors.uniqueinds\"\n\njulia> filter(x -> !startswith(x, \"ITensors\"), finddocs(\"indices\", \"set difference\"))\n1-element Array{SubString{String},1}:\n \"Base.setdiff\"","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Ideally we could have apropos do a \"smart\" Google-like search of the appropriate docstrings, but this is a pretty good start.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Additionally, the names function can be useful, which prints the names of all functions and types that are exported by a module. For example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> names(ITensors)\n264-element Array{Symbol,1}:\n Symbol(\"@OpName_str\")\n Symbol(\"@SiteType_str\")\n Symbol(\"@StateName_str\")\n Symbol(\"@TagType_str\")\n Symbol(\"@disable_warn_order\")\n Symbol(\"@reset_warn_order\")\n Symbol(\"@set_warn_order\")\n Symbol(\"@ts_str\")\n :AbstractObserver\n :OpSum\n :DMRGObserver\n :ITensor\n :ITensors\n :Index\n[...]","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Of course this is a very long list (and the methods are returned as Symbols, which are like strings but not as easy to work with). However, we can convert the list to strings and filter the strings to find functions we are interested in, for example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> filter(x -> contains(x, \"common\") && contains(x, \"ind\"), String.(names(ITensors)))\n8-element Array{String,1}:\n \"common_siteind\"\n \"common_siteinds\"\n \"commonind\"\n \"commonindex\"\n \"commoninds\"\n \"hascommoninds\"\n \"noncommonind\"\n \"noncommoninds\"","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Julia types do not have member functions, so people coming from object oriented programming languages may find that at first it is more difficult to find methods that are applicable to a certain type. However, Julia has many fantastic tools for introspection that we can use to make this task easier.","category":"page"},{"location":"AdvancedUsageGuide.html#Make-a-small-project-based-on-ITensors.jl","page":"Advanced Usage Guide","title":"Make a small project based on ITensors.jl","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Once you start to have longer code, you will want to put your code into one or more files. For example, you may have a short script with one or more functions based on ITensors.jl:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"# my_itensor_script.jl\nusing ITensors\n\nfunction norm2(A::ITensor)\n return (A*dag(A))[]\nend","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Then, in the same directory as your script my_itensor_script.jl, just type:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> include(\"my_itensor_script.jl\");\n\njulia> i = Index(2; tags=\"i\");\n\njulia> A = randomITensor(i', i);\n\njulia> norm2(A)\n[...]","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"As your code gets longer, you can split it into multiple files and include this files into one main project file, for example if you have two files with functions in them:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"# file1.jl\n\nfunction norm2(A::ITensor)\n return (A*dag(A))[]\nend","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"and","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"# file2.jl\n\nfunction square(A::ITensor)\n return A .^ 2\nend","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"# my_itensor_project.jl\n\nusing ITensors\n\ninclude(\"file1.jl\")\n\ninclude(\"file2.jl\")","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Then, as before, you can use your functions at the Julia REPL by just including the file my_itensor_project.jl:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> include(\"my_itensor_project.jl\");\n\njulia> i = Index(2; tags=\"i\");\n\njulia> A = randomITensor(i', i);\n\njulia> norm2(A)\n[...]\n\njulia> square(A)\n[...]","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"As your code gets more complicated and has more files, it is helpful to organize it into a package. That will be covered in the next section.","category":"page"},{"location":"AdvancedUsageGuide.html#Make-a-Julia-package-based-on-ITensors.jl","page":"Advanced Usage Guide","title":"Make a Julia package based on ITensors.jl","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"In this section, we will describe how to make a Julia package based on ITensors.jl. This is useful to do when your project gets longer, since it helps with:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Code organization.\nAdding dependencies that will get automatically installed through Julia's package system.\nVersioning.\nAutomated testing.\nCode sharing and easier package installation.\nOfficially registering your package with Julia.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"and many more features that we will mention later.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Start up Julia and install PkgTemplates","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"$ julia\n\njulia> ]\n\npkg> add PkgTemplates","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"then press backspace and type:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using PkgTemplates\n\njulia> t = Template(; user=\"your_github_username\", plugins=[Git(; ssh=true),])\n\njulia> t(\"MyITensorsPkg\")","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"You should put your Github account name instead of \"your_github_username\", if you want to use Github to host your package. The option plugins=[Git(; ssh=true),] sets the Github authentication to use ssh, which is generally more convenient. You can switch to https (where you have to type your username and password to push changes) by setting ssh=false or leaving off plugins=[...]. By default, the package will be located in the directory ~/.julia/dev, you can change this with the keyword argument dir=[...]. However, ~/.julia/dev is recommended since that is the directory Julia's package manager (and other packages like Revise) will look for development packages. Please see the PkgTemplate documentation for more customization options.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Then, we want to tell Julia about our new package. We do this as follows:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> ]\n\npkg> dev ~/.julia/dev/MyITensorsPkg","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"then you can do:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using MyITensorsPkg","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"from any directory to use your new package. However, it doesn't have any functions available yet. Additionally, there should be an empty test file already set up here:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"~/.julia/dev/MyITensorsPkg/test/runtests.jl","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"which you can run from any directory like:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> ]\n\npkg> test MyITensorsPkg","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"It should show something like:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"[...]\nTest Summary: |\nMyITensorsPkg.jl | No tests\n Testing MyITensorsPkg tests passed","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"since there are no tests yet.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"First we want to add ITensors as a dependency of our package. We do this by \"activating\" our package environment and then adding ITensors:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> ]\n\npkg> activate MyITensorsPkg\n\n(MyITensorsPkg) pkg> add ITensors","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This will edit the file ~/.julia/dev/MyITensorsPkg/Project.toml and add the line","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"[deps]\nITensors = \"9136182c-28ba-11e9-034c-db9fb085ebd5\"","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Because your package is under development, back in the main Pkg environment you should type resolve:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"(MyITensorsPkg) pkg> activate\n\npkg> resolve","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Now, if you or someone else uses the package, it will automatically install ITensors.jl for you.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Now your package is set up to develop! Try editing the file ~/.julia/dev/MyITensorsPkg/src/MyITensorsPkg.jl and add the norm2 function, which calculates the squared norm of an ITensor:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"module MyITensorsPkg\n\nusing ITensors\n\nexport norm2\n\nnorm2(A::ITensor) = (A*dag(A))[]\n\nend","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"The export command makes norm2 available in the namespace without needing to type MyITensorsPkg.norm2 when you do using MyITensorsPkg. Now in a new Julia session you can do:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using ITensors\n\njulia> i = Index(2)\n(dim=2|id=263)\n\njulia> A = randomITensor(i)\nITensor ord=1 (dim=2|id=263)\nNDTensors.Dense{Float64,Array{Float64,1}}\n\njulia> norm(A)^2\n6.884457016011188\n\njulia> norm2(A)\nERROR: UndefVarError: norm2 not defined\n[...]\n\njulia> using MyITensorsPkg\n\njulia> norm2(A)\n6.884457016011188","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Unfortunately, if you continue to edit the file MyITensorsPkg.jl, even if you type using MyITensorsPkg again, if you are in the same Julia session the changes will not be reflected, and you will have to restart your Julia session. The Revise package will allow you to edit your package files and have the changes reflected in real time in your current Julia session, so you don't have to restart the session.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Now, we can add some tests for our new functionality. Edit the file ~/.julia/dev/MyITensorsPkg/test/runtests.jl to look like:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"using MyITensorsPkg\nusing ITensors\nusing Test\n\n@testset \"MyITensorsPkg.jl\" begin\n i = Index(2)\n A = randomITensor(i)\n @test isapprox(norm2(A), norm(A)^2)\nend","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Now when you test your package you should see:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"pkg> test MyITensorsPkg\n[...]\nTest Summary: | Pass Total\nMyITensorsPkg.jl | 1 1\n Testing MyITensorsPkg tests passed","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Your package should already be set up as a git repository by the PkgTemplates commands we started with. We recommend using Github or similar versions control systems for your packages, especially if you plan to make them public and officially register them as Julia packages.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"You can set up your local package as a Github repository by following the steps here. Many of the steps may be unnecessary since they were already set up by PkgTemplates. You should be able to go to the website here, create a new Github repository with the name MyITensorsPkg.jl, and then following the instructions under \"push an existing repository from the command line\".","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"You may also want to switch between HTTPS and SSH authentication as described here, if you didn't choose your preferred authentication protocol with PkgTemplates.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"There are many more features you can add to your package through various Julia packages and Github, for example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Control of precompilation with tools like SnoopCompile.\nAutomatic testing of your package at every pull request/commit with Github Actions, Travis, or similar services.\nAutomated benchmarking of your package at every pull request with BenchmarkTools, PkgBenchmark and BenchmarkCI.\nAutomated building of your documentation with Documenter.\nCompiling your package with PackageCompiler.\nAutomatically check what parts of your code your tests check with code coverage.\nOfficially register your Julia package so that others can easily install it and follow along with updated versions using the Registrator.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"You can take a look at the ITensors Github page for inspiration on setting up some of these services and ideas for organizing your package.","category":"page"},{"location":"AdvancedUsageGuide.html#Developing-ITensors.jl","page":"Advanced Usage Guide","title":"Developing ITensors.jl","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This section is for someone who is interested in modifying the source code of ITensors.jl, and then possibly contribute you changes to the official ITensors.jl package.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This should not be necessary for most people. If for whatever reason you think that the functionality of ITensors.jl needs to be modified, oftentimes you can add new functions outside of ITensors.jl or directly overload a function of ITensors.jl (for example with the import keyword).","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"However, if you would like to only modify parts of the internals of an ITensors.jl function, and/or plan to contribute changes like bug fixes or new features to the official ITensors.jl package, this section is for you.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"If you install a package like ITensors with the package manager using the standard Pkg.add command:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using Pkg\n\njulia> Pkg.add(\"ITensors\")","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"it will automatically clone the latest registered/tagged version of ITensors in a randomly generated directory inside ~/.julia/packages. You can find out what version you are using with Pkg.status:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> Pkg.status(\"ITensors\")\n Status `~/.julia/environments/v1.7/Project.toml`\n [9136182c] ITensors v0.2.16","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"and you can use pkgdir to find out the directory of the source code of a package that you have loaded:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using ITensors\n\njulia> pkgdir(ITensors)\n\"/home/mfishman/.julia/packages/ITensors/cu9Bo\"","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"The source code of a package loaded in this way is read-only, so you won't be able to modify it.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"If you want to modify the source code of ITensors.jl, you should check out the packages NDTensors.jl and ITensors.jl in development mode with Pkg.develop:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> Pkg.develop([\"NDTensors\", \"ITensors\"])\nPath `/home/mfishman/.julia/dev/ITensors` exists and looks like the correct repo. Using existing path.\n Resolving package versions...\n Updating `~/.julia/environments/v1.7/Project.toml`\n [9136182c] ~ ITensors v0.2.16 ⇒ v0.2.16 `~/.julia/dev/ITensors`\n [23ae76d9] ~ NDTensors v0.1.35 ⇒ v0.1.35 `~/.julia/dev/ITensors/NDTensors`\n Updating `~/.julia/environments/v1.7/Manifest.toml`\n [9136182c] ~ ITensors v0.2.16 ⇒ v0.2.16 `~/.julia/dev/ITensors`\n [23ae76d9] ~ NDTensors v0.1.35 ⇒ v0.1.35 `~/.julia/dev/ITensors/NDTensors`\n\njulia> Pkg.status([\"NDTensors\", \"ITensors\"])\n Status `~/.julia/environments/v1.7/Project.toml`\n [9136182c] ITensors v0.2.16 `~/.julia/dev/ITensors`\n [23ae76d9] NDTensors v0.1.35 `~/.julia/dev/ITensors/NDTensors`","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Then, Julia will use the version of ITensors.jl living in the directory ~/.julia/dev/ITensors and the version of NDTensors.jl living in the directory ~/.julia/dev/ITensors/NDTensors, though you may need to restart Julia for this to take affect.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"We recommend checking out the development versions of both NDTensors.jl and ITensors.jl since we often develop both packages tandem, so the development branch of ITensors.jl may rely on changes we make in NDTensors.jl.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"By default, when you modify code in ~/.julia/dev/ITensors or ~/.julia/dev/ITensors/NDTensors you will need to restart Julia for the changes to take affect. A way around this issue is the Revise package. We highly recommend using the Revise package when you are developing packages, which automatically detects changes you are making to a package you have checked out for development and edit code and not have to restart your Julia session. In short, if you have Revise.jl loaded, you can edit the code in ~/.julia/dev/ITensors or ~/.julia/dev/ITensors/NDTensors and the changes you make will be reflected on the fly as you use the package (there are some limitations, for example you will need to restart Julia if you change the definitions of types).","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Note that the code in ~/.julia/dev/ITensors is just a git repository cloned from the repository https://github.com/ITensor/ITensors.jl, so you can do anything that you would with any other git repository (use forks of the project, check out branches, push and pull changes, etc.).","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"The standard procedure for submitting a bug fix or new feature to ITensors.jl would then be to first fork the ITensors.jl repository. Then, check out your fork for development with:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using Pkg\n\njulia> Pkg.develop(url=\"https://github.com/mtfishman/ITensors.jl\")","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"where you would replace mtfishman with your own Github username. Make the changes to the code in ~/.julia/dev/ITensors, push the changes to your fork, and then make a pull request to the ITensors.jl Github repository.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"To go back to the official version of the NDTensors.jl and ITensors.jl packages, you can use the command Pkg.free([\"NDTensors\", \"ITensors\"]):","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> Pkg.free([\"NDTensors\", \"ITensors\"])\n Resolving package versions...\n Updating `~/.julia/environments/v1.7/Project.toml`\n [9136182c] ~ ITensors v0.2.16 `~/.julia/dev/ITensors` ⇒ v0.2.16\n [23ae76d9] ~ NDTensors v0.1.35 `~/.julia/dev/ITensors/NDTensors` ⇒ v0.1.35\n Updating `~/.julia/environments/v1.7/Manifest.toml`\n [9136182c] ~ ITensors v0.2.16 `~/.julia/dev/ITensors` ⇒ v0.2.16\n [23ae76d9] ~ NDTensors v0.1.35 `~/.julia/dev/ITensors/NDTensors` ⇒ v0.1.35\n\njulia> Pkg.status([\"NDTensors\", \"ITensors\"])\n Status `~/.julia/environments/v1.7/Project.toml`\n [9136182c] ITensors v0.2.16\n [23ae76d9] NDTensors v0.1.35","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"so it returns to the version of the package you would have just after installing with Pkg.add.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Some of the Julia package development workflow definitely takes some getting used to, but once you figure out the \"flow\" and have a picture of what is going on there are only a small set of commands you really need to use.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"A small note is that we follow the Blue style guide for formatting the source code in ITensors.jl. To make this more automated, we use the wonderful package JuliaFormatter.jl. To format your developed version of ITensors.jl, all you have to do is change your directory to ~/.julia/dev/ITensors and run the command format(\".\") after loading the JuliaFormatter package:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using Pkg\n\njulia> Pkg.status(\"ITensors\")\n Status `~/.julia/environments/v1.7/Project.toml`\n [9136182c] ITensors v0.2.16 `~/.julia/dev/ITensors`\n\njulia> using ITensors\n\njulia> pkgdir(ITensors)\n\"/home/mfishman/.julia/dev/ITensors\"\n\njulia> cd(pkgdir(ITensors))\n\njulia> using JuliaFormatter\n\njulia> format(\".\")\nfalse\n\njulia> format(\".\") # Check the formatting succeeded\ntrue","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This will automatically change the style of the code according to the Blue style guide. The format command returns false if the code was not already formatted (and therefore if the command made changes to the source code to follow the style guide), and returns true otherwise.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"If you make changes to ITensors that you think will be useful to others, such as fixing bugs or adding new features, please consider making a pull request. However, please ask us first before doing so – either by raising an issue on Github or asking a question on the ITensor support forum – to make sure it is a change or addition that we will want to include or to check that it is not something we are currently working on. Coordinating with us in that way will help save your time and energy as well as ours!","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Here is a great introduction to Julia package development as well as making pull requests to existing Julia packages by the irreplacable Chris Rackauckas.","category":"page"},{"location":"AdvancedUsageGuide.html#Compiling-ITensors.jl","page":"Advanced Usage Guide","title":"Compiling ITensors.jl","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"You might notice that the time to load ITensors.jl (with using ITensors) and the time to run your first few ITensor commands is slow. This is due to Julia's just-in-time (JIT) compilation. Julia is compiling special versions of each function that is being called based on the inputs that it gets at runtime. This allows it to have fast code, often nearly as fast as fully compiled languages like C++, while still being a dynamic language.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"However, the long startup time can still be annoying. In this section, we will discuss some strategies that can be used to minimize this annoyance, for example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Precompilation.\nStaying in the same Julia session with Revise.\nUsing PackageCompiler to compile ITensors.jl ahead of time.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Precompilation is performed automatically when you first install ITensors.jl or update a version and run the command using ITensors for the first time. For example, when you first use ITensors after installation or updating, you will see:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using ITensors\n[ Info: Precompiling ITensors [9136182c-28ba-11e9-034c-db9fb085ebd5]","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"The process is done automatically, and puts some compiled binaries in your ~/.julia directory. The goal is to decrease the time it takes when you first type using ITensors in your next Julia session, and also the time it takes for you to first run ITensor functions in a new Julia session. This helps the startup time, but currently doesn't help enough. This is something both ITensors.jl and the Julia language will try to improve over time.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"To avoid this time, it is recommended that you work as much as you can in a single Julia session. You should not need to restart your Julia session very often. For example, if you are writing code in a script, just include the file again which will pull in the new changes to the script (the exception is if you change the definition of a type you made, which would requiring restarting the REPL).","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"If you are working on a project, we highly recommend using the Revise package which automatically detects changes you are making in your packages and reflects them real-time in your current REPL session. Using these strategies should minimize the number of times you need to restart your REPL session.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"If you plan to use ITensors.jl directly from the command line (i.e. not from the REPL), and the startup time is an issue, you can try compiling ITensors.jl using PackageCompiler.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Before using PackageCompiler to compile ITensors, when we first start using ITensors.jl we might see:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> @time using ITensors\n 3.845253 seconds (10.96 M allocations: 618.071 MiB, 3.95% gc time)\n\njulia> @time i = Index(2);\n 0.000684 seconds (23 allocations: 20.328 KiB)\n\njulia> @time A = randomITensor(i', i);\n 0.071022 seconds (183.24 k allocations: 9.715 MiB)\n\njulia> @time svd(A, i');\n 5.802053 seconds (24.56 M allocations: 1.200 GiB, 7.83% gc time)\n\njulia> @time svd(A, i');\n 0.000177 seconds (450 allocations: 36.609 KiB)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"ITensors provides the command ITensors.compile() to create what is called a \"custom system image\", a custom version of Julia that includes a compiled version of ITensors (see the PackageCompiler documentation for more details). Just run the command:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> ITensors.compile()\n[...]","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"By default, this will create the file sys_itensors.so in the directory ~/.julia/sysimages. Then if we start julia with:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"$ julia --sysimage ~/.julia/sysimages/sys_itensors.so","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"then you should see something like:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> @time using ITensors\n 0.330587 seconds (977.61 k allocations: 45.807 MiB, 1.89% gc time)\n\njulia> @time i = Index(2);\n 0.000656 seconds (23 allocations: 20.328 KiB)\n\njulia> @time A = randomITensor(i', i);\n 0.000007 seconds (7 allocations: 576 bytes)\n\njulia> @time svd(A, i');\n 0.263526 seconds (290.02 k allocations: 14.220 MiB)\n\njulia> @time svd(A, i');\n 0.000135 seconds (350 allocations: 29.984 KiB)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"which is much better.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Note that you will have to recompile ITensors with the command ITensors.compile() any time that you update the version of ITensors in order to keep the system image updated. We hope to make this process more automated in the future.","category":"page"},{"location":"AdvancedUsageGuide.html#Benchmarking-and-profiling","page":"Advanced Usage Guide","title":"Benchmarking and profiling","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Julia has great built-in tools for benchmarking and profiling. For benchmarking fast code at the command line, you can use BenchmarkTools:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using ITensors;\n\njulia> using BenchmarkTools;\n\njulia> i = Index(100, \"i\");\n\njulia> A = randomITensor(i, i');\n\njulia> @btime 2*$A;\n 4.279 μs (8 allocations: 78.73 KiB)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"We recommend packages like ProfileView to get detailed profiles of your code, in order to pinpoint functions or lines of code that are slower than they should be.","category":"page"},{"location":"AdvancedUsageGuide.html#ITensor-type-design-and-writing-performant-code","page":"Advanced Usage Guide","title":"ITensor type design and writing performant code","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Advanced users might notice something strange about the definition of the ITensor type, that it is often not \"type stable\". Some of this is by design. The definition for ITensor is:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"mutable struct ITensor\n inds::IndexSet\n store::TensorStorage\nend","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"These are both abstract types, which is something that is generally discouraged for peformance.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This has a few disadvantages. Some code that you might expect to be type stable, like getindex, is not, for example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> i = Index(2, \"i\");\n\njulia> A = randomITensor(i, i');\n\njulia> @code_warntype A[i=>1, i'=>2]\nVariables\n #self#::Core.Compiler.Const(getindex, false)\n T::ITensor\n ivs::Tuple{Pair{Index{Int64},Int64}}\n p::Tuple{Union{Nothing, Int64}}\n vals::Tuple{Any}\n\nBody::Number\n1 ─ %1 = NDTensors.getperm::Core.Compiler.Const(NDTensors.getperm, false)\n│ %2 = ITensors.inds(T)::IndexSet{1,IndexT,DataT} where DataT<:Tuple where IndexT<:Index\n│ %3 = Base.broadcasted(ITensors.ind, ivs)::Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple},Nothing,typeof(ind),Tuple{Tuple{Pair{Index{Int64},Int64}}}}\n│ %4 = Base.materialize(%3)::Tuple{Index{Int64}}\n│ (p = (%1)(%2, %4))\n│ %6 = NDTensors.permute::Core.Compiler.Const(NDTensors.permute, false)\n│ %7 = Base.broadcasted(ITensors.val, ivs)::Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple},Nothing,typeof(val),Tuple{Tuple{Pair{Index{Int64},Int64}}}}\n│ %8 = Base.materialize(%7)::Tuple{Int64}\n│ (vals = (%6)(%8, p))\n│ %10 = Core.tuple(T)::Tuple{ITensor}\n│ %11 = Core._apply_iterate(Base.iterate, Base.getindex, %10, vals)::Number\n│ %12 = Core.typeassert(%11, ITensors.Number)::Number\n└── return %12\n\njulia> typeof(A[i=>1, i'=>2])\nFloat64","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Uh oh, that doesn't look good! Julia can't know ahead of time, based on the inputs, what the type of the output is, besides that it will be a Number (though at runtime, the output has a concrete type, Float64).","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"So why is it designed this way? The main reason is to allow more generic and dynamic code than traditional, statically-typed Arrays. This allows us to have code like:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> i = Index(2, \"i\")\n(dim=2|id=811|\"i\")\n\njulia> A = emptyITensor(i', i);\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=811|\"i\")'\nDim 2: (dim=2|id=811|\"i\")\nNDTensors.Empty{Float64,NDTensors.Dense{Float64,Array{Float64,1}}}\n 2×2\n\n\n\njulia> A[i' => 1, i => 2] = 1.2;\n\njulia> @show A;\nA = ITensor ord=2\nDim 1: (dim=2|id=811|\"i\")'\nDim 2: (dim=2|id=811|\"i\")\nNDTensors.Dense{Float64,Array{Float64,1}}\n 2×2\n 0.0 1.2\n 0.0 0.0","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Here, the type of the storage of A is changed in-place. It starts as an Empty storage, a special trivial storage. When we set an element, we then allocate the appropriate storage. Allocations are performed only when needed, so if another element is set then no allocation is performed. More generally, this allows ITensors to have more generic in-place functionality, so you can write code where you don't know what the storage is until runtime.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This can lead to certain types of code having perfomance problems, for example looping through ITensors with many elements can be slow:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> function myscale!(A::ITensor, x::Number)\n for n in 1:dim(A)\n A[n] = x * A[n]\n end\n end;\n\njulia> d = 10_000;\n\njulia> i = Index(d);\n\njulia> @btime myscale!(A, 2) setup = (A = randomITensor(i));\n 2.169 ms (117958 allocations: 3.48 MiB)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"However, this is fast:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> function myscale!(A::Array, x::Number)\n for n in 1:length(A)\n A[n] = x * A[n]\n end\n end;\n\njulia> @btime myscale!(A, 2) setup = (A = randn(d));\n 3.451 μs (0 allocations: 0 bytes)\n\njulia> myscale2!(A::ITensor, x::Number) = myscale!(array(A), x)\nmyscale2! (generic function with 1 method)\n\njulia> @btime myscale2!(A, 2) setup = (A = randomITensor(i));\n 3.571 μs (2 allocations: 112 bytes)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"How does this work? It relies on a \"function barrier\" technique. Julia compiles functions \"just-in-time\", so that calls to an inner function written in terms of a type-stable type are still fast. That inner function is compiled to very fast code. The main overhead is that Julia has to determine which function to call at runtime.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Therefore, users should keep this in mind when they are writing ITensors.jl code, and we warn that explicitly looping over large ITensors by individual elements should be done with caution in performance critical sections of your code. However, be sure to benchmark and profile your code before prematurely optimizing, since you may be surprised about what are the fast and slow parts of your code.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Some strategies for avoiding ITensor loops are:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Use broadcasting and other built-in ITensor functionality that makes use of function barriers.\nConvert ITensors to type-stable collections like the Tensor type of NDTensors.jl and write functions in terms of the Tensor type (i.e. the function barrier techique that is used throughout ITensors.jl).\nWhen initializing very large ITensors elementwise, use built-in ITensor constructors, or first construct an equivalent tensor as an Array or Tensor and then convert it to an ITensor.","category":"page"},{"location":"AdvancedUsageGuide.html#ITensor-in-place-operations","page":"Advanced Usage Guide","title":"ITensor in-place operations","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"In-place operations can help with optimizing code, when the memory of the output tensor of an operation is preallocated.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"The main way to access this in ITensor is through broadcasting. For example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"A = randomITensor(i, i')\nB = randomITensor(i', i)\nA .+= 2 .* B","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Internally, this is rewritten by Julia as a call to broadcast!. ITensors.jl overloads this call (or more specifically, a lower level function copyto! written in terms of a special lazy type that saves all of the objects and operations). Then, this call is rewritten as","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"map!((x,y) -> x+2*y, A, A, B)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"This is mostly an optimization to use when you can preallocate storage that can be used multiple times.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Additionally, ITensors makes the unique choice that:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"C .= A .* B","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"is interpreted as an in-place tensor contraction. What this means is that this calls a function:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"mul!(C, A, B)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"(likely to be given an alternative name contract!) which contracts A and B into the pre-allocated memory C.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"Because of the design of the ITensor type (see the section above), there is some flexibility we take in allocating memory for users. For example, if the storage type is more narrow than the result, for convenience we might expand it in-place. If you are worried about memory allocations, we recommend using benchmarking and profiling to pinpoint slow parts of your code (often times, you may be surprised by what is actually slow).","category":"page"},{"location":"AdvancedUsageGuide.html#NDTensors-and-ITensors","page":"Advanced Usage Guide","title":"NDTensors and ITensors","text":"","category":"section"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"ITensors.jl is built on top of another, more traditional tensor library called NDTensors. NDTensors implements AbstractArrays with a variety of sparse storage types, with more to come in the future.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"NDTensors implements functionality like permutation of dimensions, fast get and set index, broadcasting, and tensor contraction (where labels of the dimensions must be specified).","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"For example:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"using ITensors\nusing NDTensors\n\nT = Tensor(2,2,2)\nT[1,2,1] = 1.3 # Conventional element setting\n\ni = Index(2)\nT = Tensor((i,i',i')) # The identifiers are ignored, just interpreted as above\nT[1,2,1] = 1.3","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"To make performant ITensor code (refer to the the previous section on type stability and function barriers), ITensor storage data and indices are passed by reference into Tensors, where the performance critical operations are performed.","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"An example of a function barrier using NDTensors is the following:","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"julia> using NDTensors\n\njulia> d = 10_000;\n\njulia> i = Index(d);\n\njulia> function myscale!(A::Tensor, x::Number)\n for n in 1:dim(A)\n A[n] = x * A[n]\n end\n end;\n\njulia> @btime myscale!(A, 2) setup = (A = Tensor(d));\n 3.530 μs (0 allocations: 0 bytes)\n\njulia> myscale2!(A::ITensor, x::Number) = myscale!(tensor(A), x)\nmyscale2! (generic function with 1 method)\n\njulia> @btime myscale2!(A, 2) setup = (A = randomITensor(i));\n 3.549 μs (2 allocations: 112 bytes)","category":"page"},{"location":"AdvancedUsageGuide.html","page":"Advanced Usage Guide","title":"Advanced Usage Guide","text":"A very efficient function is written for the Tensor type. Then, the ITensor version just wraps the Tensor function by calling it after converting the ITensor to a Tensor (without any copying) with the tensor function. This is the basis for the design of all performance critical ITensors.jl functions.","category":"page"},{"location":"OpSum.html#OpSum","page":"OpSum (AutoMPO)","title":"OpSum","text":"","category":"section"},{"location":"OpSum.html#Description","page":"OpSum (AutoMPO)","title":"Description","text":"","category":"section"},{"location":"OpSum.html","page":"OpSum (AutoMPO)","title":"OpSum (AutoMPO)","text":"OpSum","category":"page"},{"location":"OpSum.html#ITensors.Ops.OpSum","page":"OpSum (AutoMPO)","title":"ITensors.Ops.OpSum","text":"An OpSum represents a sum of operator terms.\n\nOften it is used to create matrix product operator (MPO) approximation of the sum of the terms in the OpSum oject. Each term is a product of local operators specified by names such as \"Sz\" or \"N\", times an optional coefficient which can be real or complex.\n\nWhich local operator names are available is determined by the function op associated with the TagType defined by special Index tags, such as \"S=1/2\", \"S=1\", \"Fermion\", and \"Electron\".\n\n\n\n\n\n","category":"type"},{"location":"OpSum.html#Methods","page":"OpSum (AutoMPO)","title":"Methods","text":"","category":"section"},{"location":"OpSum.html","page":"OpSum (AutoMPO)","title":"OpSum (AutoMPO)","text":"add!\nMPO(::OpSum,::Vector{<:Index})","category":"page"},{"location":"OpSum.html#ITensors.ITensorMPS.add!","page":"OpSum (AutoMPO)","title":"ITensors.ITensorMPS.add!","text":"add!(opsum::OpSum,\n op1::String, i1::Int)\n\nadd!(opsum::OpSum,\n coef::Number,\n op1::String, i1::Int)\n\nadd!(opsum::OpSum,\n op1::String, i1::Int,\n op2::String, i2::Int,\n ops...)\n\nadd!(opsum::OpSum,\n coef::Number,\n op1::String, i1::Int,\n op2::String, i2::Int,\n ops...)\n\n+(opsum:OpSum, term::Tuple)\n\nAdd a single- or multi-site operator term to the OpSum opsum. Each operator is specified by a name (String) and a site number (Int). The second version accepts a real or complex coefficient.\n\nThe + operator version of this function accepts a tuple with entries either (String,Int,String,Int,...) or (Number,String,Int,String,Int,...) where these tuple values are the same as valid inputs to the add! function. For inputting a very large number of terms (tuples) to an OpSum, consider using the broadcasted operator .+= which avoids reallocating the OpSum after each addition.\n\nExamples\n\nopsum = OpSum()\n\nadd!(opsum,\"Sz\",2,\"Sz\",3)\n\nopsum += (\"Sz\",3,\"Sz\",4)\n\nopsum += (0.5,\"S+\",4,\"S-\",5)\n\nopsum .+= (0.5,\"S+\",5,\"S-\",6)\n\n\n\n\n\n","category":"function"},{"location":"OpSum.html#ITensors.ITensorMPS.MPO-Tuple{ITensors.LazyApply.Applied{typeof(sum), Tuple{Array{ITensors.LazyApply.Applied{typeof(*), Tuple{C, Prod{Op}}, @NamedTuple{}}, 1}}, @NamedTuple{}} where C, Vector{<:Index}}","page":"OpSum (AutoMPO)","title":"ITensors.ITensorMPS.MPO","text":"MPO(os::OpSum, sites::Vector{<:Index}; splitblocks=true, kwargs...)\nMPO(eltype::Type{<:Number}, os::OpSum, sites::Vector{<:Index}; splitblocks=true, kwargs...)\n\nConvert an OpSum object os to an MPO, with indices given by sites. The resulting MPO will have the indices sites[1], sites[1]', sites[2], sites[2]' etc. The conversion is done by an algorithm that compresses the MPO resulting from adding the OpSum terms together, often achieving the minimum possible bond dimension.\n\nOptionally specify the desired element type of the output MPO by passing the type as the first argument.\n\nThe keyword argument splitblocks controls the sparsity of the resulting MPO. With the default splitblocks=true, the link indices of the MPO are split into blocks of dimension 1, potentially making the MPO more sparse.\n\nWith the splitblocks=false, the blocks of the link dimensions are packed as much as possible according to common quantum numbers, making larger blocks. Before ITensors 0.3.19, this was the default output, but we have found that in general MPOs output with splitblocks=true lead to better performance in algorithms like DMRG.\n\nExamples\n\nos = OpSum()\nos += \"Sz\",1,\"Sz\",2\nos += \"Sz\",2,\"Sz\",3\nos += \"Sz\",3,\"Sz\",4\n\nsites = siteinds(\"S=1/2\",4)\nH = MPO(os,sites)\nH = MPO(Float32,os,sites)\nH = MPO(os,sites; splitblocks=false)\n\n\n\n\n\n","category":"method"},{"location":"getting_started/Installing.html#Installing-Julia-and-ITensor","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"","category":"section"},{"location":"getting_started/Installing.html#Installing-Julia-Locally-and-On-a-Cluster","page":"Installing Julia and ITensor","title":"Installing Julia Locally and On a Cluster","text":"","category":"section"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Because Julia is a new language, it is usually not pre-installed on machines such as supercomputing clusters. But it is easy to install yourself both on your own machine and in your supercomputing environment. Here we will briefly cover installing Julia on your own machine, then discuss setting it up yourself on a supercomputer.","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Installing Julia on Your Own Machine","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"To install the Julia language, visit https://julialang.org/downloads/ for downloads and installation instructions. Or consider using your system's package manager.","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Cluster Install of Julia and ITensor","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"If you would like to use Julia on a remote cluster, such as at many labs or universities, but Julia is not available system-wide, you can still easily install your own local version of Julia. A local install will offer the same performance and features (package manager, etc.) as a system-wide install, and you can upgrade it at your own pace.","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Once you set up Julia in your cluster account, you can install ITensor in the same way as on your personal computer (see next section on installing ITensor).","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"To install Julia locally within your cluster account, follow these basic steps (details will vary depending on your setup):","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Download a binary version of Julia here. On a remote Unix or Linux cluster, you can use the program wget to download remote files. (Right click on the link on the Julia downloads page to the Generic Linux on x86, 64-bit Julia download to copy the link to pass to the wget program.)\nUse the tar program to uncompress the .tar.gz file you have downloaded.\nCreate a soft link somewhere in your PATH (such as in the bin/ subfolder of your home folder, which you might need to create) pointing to the file \"bin/julia\" inside of the uncompressed Julia folder you just created.","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"For example, the set of commands might look like this (where these commands are assumed to be executed in your home directory):","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"$ cd\n$ mkdir -p bin\n$ wget https://julialang-s3.julialang.org/bin/linux/x64/1.7/julia-1.7.2-linux-x86_64.tar.gz\n$ tar xvzf julia-1.7.2-linux-x86_64.tar.gz\n$ ln -s julia-1.7.2/bin/julia bin/julia","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"If you want to install Julia 1.6.6, you would change 1.7 to 1.6 and 1.7.2 to 1.6.6. In general we recommend using the current stable release of Julia, which you can find out by going to the Julia Downloads page. We also don't recommend using versions of Julia below 1.6, which are no longer compatible with ITensors.jl as of ITensors 0.3.","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"After these steps, you should be able to type julia from your terminal to run Julia in interactive mode. If that works, then you have the Julia language and can run it in all the usual ways. If it does not work, you may need to log out and back in, and check that the bin directory is in your program execution path (PATH environment variable).","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Explanation of the sample commands above:","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"The first command cd goes to your home directory.\nThe second command makes a new folder bin/ under your home directory if it does not already exist.\nThe third command downloads the Julia language as a compressed tar.gz file. (You may want to do this step and the follwing steps in a different folder of your choosing.)\nThe fourth command uncompresses the tar.gz file into a folder called (in this example) julia-1.7.2.\nThe last command makes a soft link called julia in your bin directory which links to the Julia language binary within the folder you just unpacked containing the Julia language.","category":"page"},{"location":"getting_started/Installing.html#Installing-ITensor-(ITensors.jl-Package)","page":"Installing Julia and ITensor","title":"Installing ITensor (ITensors.jl Package)","text":"","category":"section"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Installing the Julia version of ITensor is easy once you have the Julia language installed. For more information about installing Julia, please see the Julia language downloads page.","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Once you have installed Julia on your machine,","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Enter the command julia to launch an interactive Julia session (a.k.a. the Julia \"REPL\")\nType ] to enter the package manager (pkg> prompt should now show)\nEnter the command add ITensors\nAfter installation completes, press backspace to return to the normal julia> prompt\nOptional but Recommended: Enter the command julia> using ITensors; ITensors.compile() to compile a large fraction of the ITensor library code and following the instructions afterward to make an alias for loading a pre-built ITensor system image with Julia. This step can take up to 10 minutes to complete but only has to be done once for each version of ITensor. See the section on compiling ITensor for more information.","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"Sample screenshot:","category":"page"},{"location":"getting_started/Installing.html","page":"Installing Julia and ITensor","title":"Installing Julia and ITensor","text":"(Image: )","category":"page"},{"location":"MPSandMPO.html#MPS-and-MPO","page":"MPS and MPO","title":"MPS and MPO","text":"","category":"section"},{"location":"MPSandMPO.html#Types","page":"MPS and MPO","title":"Types","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"MPS\nMPO","category":"page"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPS","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPS","text":"MPS\n\nA finite size matrix product state type. Keeps track of the orthogonality center.\n\n\n\n\n\n","category":"type"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPO","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPO","text":"MPO\n\nA finite size matrix product operator type. Keeps track of the orthogonality center.\n\n\n\n\n\n","category":"type"},{"location":"MPSandMPO.html#MPS-Constructors","page":"MPS and MPO","title":"MPS Constructors","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"MPS(::Int)\nMPS(::Type{<:Number}, ::Vector{<:Index})\nrandomMPS(sites::Vector{<:Index})\nrandomMPS(::Type{<:Number}, sites::Vector{<:Index})\nrandomMPS(::Vector{<:Index}, ::Any)\nMPS(::Vector{<:Index}, ::Any)\nMPS(::Type{<:Number}, ::Vector{<:Index}, ::Any)\nMPS(::Vector{<:Pair{<:Index}})\nMPS(::Type{<:Number}, ::Vector{<:Pair{<:Index}})","category":"page"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPS-Tuple{Int64}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPS","text":"MPS(N::Int)\n\nConstruct an MPS with N sites with default constructed ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPS-Tuple{Type{<:Number}, Vector{<:Index}}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPS","text":"MPS([::Type{ElT} = Float64, ]sites; linkdims=1)\n\nConstruct an MPS filled with Empty ITensors of type ElT from a collection of indices.\n\nOptionally specify the link dimension with the keyword argument linkdims, which by default is 1.\n\nIn the future we may generalize linkdims to allow specifying each individual link dimension as a vector, and additionally allow specifying quantum numbers.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.randomMPS-Tuple{Vector{<:Index}}","page":"MPS and MPO","title":"ITensors.ITensorMPS.randomMPS","text":"randomMPS(sites::Vector{<:Index}; linkdims=1)\nrandomMPS(eltype::Type{<:Number}, sites::Vector{<:Index}; linkdims=1)\n\nConstruct a random MPS with link dimension linkdims which by default has element type Float64.\n\nlinkdims can also accept a Vector{Int} with length(linkdims) == length(sites) - 1 for constructing an MPS with non-uniform bond dimension.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.randomMPS-Tuple{Type{<:Number}, Vector{<:Index}}","page":"MPS and MPO","title":"ITensors.ITensorMPS.randomMPS","text":"randomMPS(eltype::Type{<:Number}, sites::Vector{<:Index}; linkdims=1)\n\nConstruct a random MPS with link dimension linkdims of type eltype.\n\nlinkdims can also accept a Vector{Int} with length(linkdims) == length(sites) - 1 for constructing an MPS with non-uniform bond dimension.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.randomMPS-Tuple{Vector{<:Index}, Any}","page":"MPS and MPO","title":"ITensors.ITensorMPS.randomMPS","text":"randomMPS(sites::Vector{<:Index}, state; linkdims=1)\n\nConstruct a real, random MPS with link dimension linkdims, made by randomizing an initial product state specified by state. This version of randomMPS is necessary when creating QN-conserving random MPS (consisting of QNITensors). The initial state array provided determines the total QN of the resulting random MPS.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPS-Tuple{Vector{<:Index}, Any}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPS","text":"MPS(sites::Vector{<:Index},states)\n\nConstruct a product state MPS having site indices sites, and which corresponds to the initial state given by the array states. The states array may consist of either an array of integers or strings, as recognized by the state function defined for the relevant Index tag type.\n\nExamples\n\nN = 10\nsites = siteinds(\"S=1/2\", N)\nstates = [isodd(n) ? \"Up\" : \"Dn\" for n in 1:N]\npsi = MPS(sites, states)\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPS-Tuple{Type{<:Number}, Vector{<:Index}, Any}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPS","text":"MPS(::Type{T},\n sites::Vector{<:Index},\n states::Union{Vector{String},\n Vector{Int},\n String,\n Int})\n\nConstruct a product state MPS of element type T, having site indices sites, and which corresponds to the initial state given by the array states. The input states may be an array of strings or an array of ints recognized by the state function defined for the relevant Index tag type. In addition, a single string or int can be input to create a uniform state.\n\nExamples\n\nN = 10\nsites = siteinds(\"S=1/2\", N)\nstates = [isodd(n) ? \"Up\" : \"Dn\" for n in 1:N]\npsi = MPS(ComplexF64, sites, states)\nphi = MPS(sites, \"Up\")\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPS-Tuple{Vector{<:Pair{<:Index}}}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPS","text":"MPS(ivals::Vector{<:Pair{<:Index}})\n\nConstruct a product state MPS with element type Float64 and nonzero values determined from the input IndexVals.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPS-Tuple{Type{<:Number}, Vector{<:Pair{<:Index}}}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPS","text":"MPS(::Type{T<:Number}, ivals::Vector{<:Pair{<:Index}})\n\nConstruct a product state MPS with element type T and nonzero values determined from the input IndexVals.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#MPO-Constructors","page":"MPS and MPO","title":"MPO Constructors","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"MPO(::Int)\nMPO(::Type{<:Number}, ::Vector{<:Index}, ::Vector{String})\nMPO(::Type{<:Number}, ::Vector{<:Index}, ::String)","category":"page"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPO-Tuple{Int64}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPO","text":"MPO(N::Int)\n\nMake an MPO of length N filled with default ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPO-Tuple{Type{<:Number}, Vector{<:Index}, Vector{String}}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPO","text":"MPO([::Type{ElT} = Float64}, ]sites, ops::Vector{String})\n\nMake an MPO with pairs of sites s[i] and s[i]' and operators ops on each site.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.MPO-Tuple{Type{<:Number}, Vector{<:Index}, String}","page":"MPS and MPO","title":"ITensors.ITensorMPS.MPO","text":"MPO([::Type{ElT} = Float64, ]sites, op::String)\n\nMake an MPO with pairs of sites s[i] and s[i]' and operator op on every site.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Copying-behavior","page":"MPS and MPO","title":"Copying behavior","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"copy(::ITensors.AbstractMPS)\ndeepcopy(::ITensors.AbstractMPS)","category":"page"},{"location":"MPSandMPO.html#Base.copy-Tuple{AbstractMPS}","page":"MPS and MPO","title":"Base.copy","text":"copy(::MPS)\ncopy(::MPO)\n\nMake a shallow copy of an MPS or MPO. By shallow copy, it means that a new MPS/MPO is returned, but the data of the tensors are still shared between the returned MPS/MPO and the original MPS/MPO.\n\nTherefore, replacing an entire tensor of the returned MPS/MPO will not modify the input MPS/MPO, but modifying the data of the returned MPS/MPO will modify the input MPS/MPO.\n\nUse deepcopy for an alternative that copies the ITensors as well.\n\nExamples\n\njulia> using ITensors\n\njulia> s = siteinds(\"S=1/2\", 3);\n\njulia> M1 = randomMPS(s; linkdims=3);\n\njulia> norm(M1)\n0.9999999999999999\n\njulia> M2 = copy(M1);\n\njulia> M2[1] *= 2;\n\njulia> norm(M1)\n0.9999999999999999\n\njulia> norm(M2)\n1.9999999999999998\n\njulia> M3 = copy(M1);\n\njulia> M3[1] .*= 3; # Modifies the tensor data\n\njulia> norm(M1)\n3.0000000000000004\n\njulia> norm(M3)\n3.0000000000000004\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Base.deepcopy-Tuple{AbstractMPS}","page":"MPS and MPO","title":"Base.deepcopy","text":"deepcopy(::MPS)\ndeepcopy(::MPO)\n\nMake a deep copy of an MPS or MPO. By deep copy, it means that a new MPS/MPO is returned that doesn't share any data with the input MPS/MPO.\n\nTherefore, modifying the resulting MPS/MPO will note modify the original MPS/MPO.\n\nUse copy for an alternative that performs a shallow copy that avoids copying the ITensor data.\n\nExamples\n\njulia> using ITensors\n\njulia> s = siteinds(\"S=1/2\", 3);\n\njulia> M1 = randomMPS(s; linkdims=3);\n\njulia> norm(M1)\n1.0\n\njulia> M2 = deepcopy(M1);\n\njulia> M2[1] .*= 2; # Modifies the tensor data\n\njulia> norm(M1)\n1.0\n\njulia> norm(M2)\n2.0\n\njulia> M3 = copy(M1);\n\njulia> M3[1] .*= 3; # Modifies the tensor data\n\njulia> norm(M1)\n3.0\n\njulia> norm(M3)\n3.0\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Properties","page":"MPS and MPO","title":"Properties","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"eltype(::ITensors.AbstractMPS)\nflux(::ITensors.AbstractMPS)\nhasqns(::ITensors.AbstractMPS)\nlength(::ITensors.AbstractMPS)\nmaxlinkdim(::ITensors.AbstractMPS)","category":"page"},{"location":"MPSandMPO.html#Base.eltype-Tuple{AbstractMPS}","page":"MPS and MPO","title":"Base.eltype","text":"eltype(m::MPS)\neltype(m::MPO)\n\nThe element type of the MPS/MPO. Always returns ITensor.\n\nFor the element type of the ITensors of the MPS/MPO, use promote_itensor_eltype.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.flux-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.flux","text":"flux(M::MPS)\n\nflux(M::MPO)\n\ntotalqn(M::MPS)\n\ntotalqn(M::MPO)\n\nFor an MPS or MPO which conserves quantum numbers, compute the total QN flux. For a tensor network such as an MPS or MPO, the flux is the sum of fluxes of each of the tensors in the network. The name totalqn is an alias for flux.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.hasqns-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.hasqns","text":"hasqns(M::MPS)\n\nhasqns(M::MPO)\n\nReturn true if the MPS or MPO has tensors which carry quantum numbers.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Base.length-Tuple{AbstractMPS}","page":"MPS and MPO","title":"Base.length","text":"length(::MPS/MPO)\n\nThe number of sites of an MPS/MPO.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.maxlinkdim-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.ITensorMPS.maxlinkdim","text":"maxlinkdim(M::MPS)\nmaxlinkdim(M::MPO)\n\nGet the maximum link dimension of the MPS or MPO.\n\nThe minimum this will return is 1, even if there are no link indices.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Obtaining-and-finding-indices","page":"MPS and MPO","title":"Obtaining and finding indices","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"siteinds(::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS, ::Int)\nsiteinds(::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS, ::Int)\nfindsite\nfindsites\nfirstsiteinds\nlinkind(::ITensors.AbstractMPS,::Int)\nsiteind(::MPS, ::Int)\nsiteind(::typeof(first), ::MPS, ::Int)\nsiteinds(::MPS)\nsiteind(::MPO, ::Int)\nsiteinds(::MPO)\nsiteinds(::ITensors.AbstractMPS, ::Int)","category":"page"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteinds-Tuple{typeof(commoninds), AbstractMPS, AbstractMPS, Int64}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteinds","text":"siteinds(commoninds, A::MPO, B::MPS, j::Integer; kwargs...)\nsiteinds(commonind, A::MPO, B::MPO, j::Integer; kwargs...)\n\nGet the site index (or indices) of the jth MPO tensor of A that is shared with MPS/MPO B.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteinds-Tuple{typeof(uniqueinds), AbstractMPS, AbstractMPS, Int64}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteinds","text":"siteinds(uniqueinds, A::MPO, B::MPS, j::Integer; kwargs...)\nsiteinds(uniqueind, A::MPO, B::MPS, j::Integer; kwargs...)\n\nGet the site index (or indices) of MPO A that is unique to A (not shared with MPS/MPO B).\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.findsite","page":"MPS and MPO","title":"ITensors.ITensorMPS.findsite","text":"findsite(M::Union{MPS, MPO}, is)\n\nReturn the first site of the MPS or MPO that has at least one Index in common with the Index or collection of indices is.\n\nTo find all sites with common indices with is, use the findsites function.\n\nExamples\n\ns = siteinds(\"S=1/2\", 5)\nψ = randomMPS(s)\nfindsite(ψ, s[3]) == 3\nfindsite(ψ, (s[3], s[4])) == 3\n\nM = MPO(s)\nfindsite(M, s[4]) == 4\nfindsite(M, s[4]') == 4\nfindsite(M, (s[4]', s[4])) == 4\nfindsite(M, (s[4]', s[3])) == 3\n\n\n\n\n\n","category":"function"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.findsites","page":"MPS and MPO","title":"ITensors.ITensorMPS.findsites","text":"findsites(M::Union{MPS, MPO}, is)\n\nReturn the sites of the MPS or MPO that have indices in common with the collection of site indices is.\n\nExamples\n\ns = siteinds(\"S=1/2\", 5)\nψ = randomMPS(s)\nfindsites(ψ, s[3]) == [3]\nfindsites(ψ, (s[4], s[1])) == [1, 4]\n\nM = MPO(s)\nfindsites(M, s[4]) == [4]\nfindsites(M, s[4]') == [4]\nfindsites(M, (s[4]', s[4])) == [4]\nfindsites(M, (s[4]', s[3])) == [3, 4]\n\n\n\n\n\n","category":"function"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.firstsiteinds","page":"MPS and MPO","title":"ITensors.ITensorMPS.firstsiteinds","text":"firstsiteinds(M::MPO; kwargs...)\n\nGet a Vector of the first site Index found on each site of M.\n\nBy default, it finds the first site Index with prime level 0.\n\n\n\n\n\n","category":"function"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.linkind-Tuple{AbstractMPS, Int64}","page":"MPS and MPO","title":"ITensors.ITensorMPS.linkind","text":"linkind(M::MPS, j::Integer)\nlinkind(M::MPO, j::Integer)\n\nGet the link or bond Index connecting the MPS or MPO tensor on site j to site j+1.\n\nIf there is no link Index, return nothing.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteind-Tuple{MPS, Int64}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteind","text":"siteind(M::MPS, j::Int; kwargs...)\n\nGet the first site Index of the MPS. Return nothing if none is found.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteind-Tuple{typeof(first), MPS, Int64}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteind","text":"siteind(::typeof(first), M::Union{MPS,MPO}, j::Integer; kwargs...)\n\nReturn the first site Index found on the MPS or MPO (the first Index unique to the jth MPS/MPO tensor).\n\nYou can choose different filters, like prime level and tags, with the kwargs.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteinds-Tuple{MPS}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteinds","text":"siteinds(M::MPS)\nsiteinds(::typeof(first), M::MPS)\n\nGet a vector of the first site Index found on each tensor of the MPS.\n\nsiteinds(::typeof(only), M::MPS)\n\nGet a vector of the only site Index found on each tensor of the MPS. Errors if more than one is found.\n\nsiteinds(::typeof(all), M::MPS)\n\nGet a vector of the all site Indices found on each tensor of the MPS. Returns a Vector of IndexSets.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteind-Tuple{MPO, Int64}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteind","text":"siteind(M::MPO, j::Int; plev = 0, kwargs...)\n\nGet the first site Index of the MPO found, by default with prime level 0.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteinds-Tuple{MPO}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteinds","text":"siteinds(M::MPO; kwargs...)\n\nGet a Vector of IndexSets of all the site indices of M.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.SiteTypes.siteinds-Tuple{AbstractMPS, Int64}","page":"MPS and MPO","title":"ITensors.SiteTypes.siteinds","text":"siteinds(M::Union{MPS, MPO}}, j::Integer; kwargs...)\n\nReturn the site Indices found of the MPO or MPO at the site j as an IndexSet.\n\nOptionally filter prime tags and prime levels with keyword arguments like plev and tags.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Priming-and-tagging","page":"MPS and MPO","title":"Priming and tagging","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"prime(::ITensors.AbstractMPS)\nprime(::typeof(siteinds), ::ITensors.AbstractMPS)\nprime(::typeof(linkinds), ::ITensors.AbstractMPS)\nprime(::typeof(siteinds), ::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\nprime(::typeof(siteinds), ::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\n\nswapprime(::ITensors.AbstractMPS, args...; kwargs...)\n\nsetprime(::ITensors.AbstractMPS)\nsetprime(::typeof(siteinds), ::ITensors.AbstractMPS)\nsetprime(::typeof(linkinds), ::ITensors.AbstractMPS)\nsetprime(::typeof(siteinds), ::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\nsetprime(::typeof(siteinds), ::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\n\nnoprime(::ITensors.AbstractMPS)\nnoprime(::typeof(siteinds), ::ITensors.AbstractMPS)\nnoprime(::typeof(linkinds), ::ITensors.AbstractMPS)\nnoprime(::typeof(siteinds), ::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\nnoprime(::typeof(siteinds), ::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\n\naddtags(::ITensors.AbstractMPS)\naddtags(::typeof(siteinds), ::ITensors.AbstractMPS)\naddtags(::typeof(linkinds), ::ITensors.AbstractMPS)\naddtags(::typeof(siteinds), ::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\naddtags(::typeof(siteinds), ::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\n\nremovetags(::ITensors.AbstractMPS)\nremovetags(::typeof(siteinds), ::ITensors.AbstractMPS)\nremovetags(::typeof(linkinds), ::ITensors.AbstractMPS)\nremovetags(::typeof(siteinds), ::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\nremovetags(::typeof(siteinds), ::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\n\nreplacetags(::ITensors.AbstractMPS)\nreplacetags(::typeof(siteinds), ::ITensors.AbstractMPS)\nreplacetags(::typeof(linkinds), ::ITensors.AbstractMPS)\nreplacetags(::typeof(siteinds), ::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\nreplacetags(::typeof(siteinds), ::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\n\nsettags(::ITensors.AbstractMPS)\nsettags(::typeof(siteinds), ::ITensors.AbstractMPS)\nsettags(::typeof(linkinds), ::ITensors.AbstractMPS)\nsettags(::typeof(siteinds), ::typeof(commoninds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)\nsettags(::typeof(siteinds), ::typeof(uniqueinds), ::ITensors.AbstractMPS, ::ITensors.AbstractMPS)","category":"page"},{"location":"MPSandMPO.html#ITensors.prime-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.prime","text":"prime[!](M::MPS, args...; kwargs...)\nprime[!](M::MPO, args...; kwargs...)\n\nApply prime to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.prime-Tuple{typeof(siteinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.prime","text":"prime[!](siteinds, M::MPS, args...; kwargs...)\nprime[!](siteinds, M::MPO, args...; kwargs...)\n\nApply prime to all site indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.prime-Tuple{typeof(linkinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.prime","text":"prime[!](linkinds, M::MPS, args...; kwargs...)\nprime[!](linkinds, M::MPO, args...; kwargs...)\n\nApply prime to all link indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.prime-Tuple{typeof(siteinds), typeof(commoninds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.prime","text":"prime[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)\nprime[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)\n\nApply prime to the site indices that are shared by M1 and M2.\n\nReturns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.prime-Tuple{typeof(siteinds), typeof(uniqueinds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.prime","text":"prime[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)\n\nApply prime to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.\n\nThe ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.swapprime-Tuple{AbstractMPS, Vararg{Any}}","page":"MPS and MPO","title":"ITensors.swapprime","text":"swapprime[!](M::MPS, args...; kwargs...)\nswapprime[!](M::MPO, args...; kwargs...)\n\nApply swapprime to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.setprime-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.setprime","text":"setprime[!](M::MPS, args...; kwargs...)\nsetprime[!](M::MPO, args...; kwargs...)\n\nApply setprime to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.setprime-Tuple{typeof(siteinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.setprime","text":"setprime[!](siteinds, M::MPS, args...; kwargs...)\nsetprime[!](siteinds, M::MPO, args...; kwargs...)\n\nApply setprime to all site indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.setprime-Tuple{typeof(linkinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.setprime","text":"setprime[!](linkinds, M::MPS, args...; kwargs...)\nsetprime[!](linkinds, M::MPO, args...; kwargs...)\n\nApply setprime to all link indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.setprime-Tuple{typeof(siteinds), typeof(commoninds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.setprime","text":"setprime[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)\nsetprime[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)\n\nApply setprime to the site indices that are shared by M1 and M2.\n\nReturns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.setprime-Tuple{typeof(siteinds), typeof(uniqueinds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.setprime","text":"setprime[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)\n\nApply setprime to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.\n\nThe ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.noprime-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.noprime","text":"noprime[!](M::MPS, args...; kwargs...)\nnoprime[!](M::MPO, args...; kwargs...)\n\nApply noprime to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.noprime-Tuple{typeof(siteinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.noprime","text":"noprime[!](siteinds, M::MPS, args...; kwargs...)\nnoprime[!](siteinds, M::MPO, args...; kwargs...)\n\nApply noprime to all site indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.noprime-Tuple{typeof(linkinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.noprime","text":"noprime[!](linkinds, M::MPS, args...; kwargs...)\nnoprime[!](linkinds, M::MPO, args...; kwargs...)\n\nApply noprime to all link indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.noprime-Tuple{typeof(siteinds), typeof(commoninds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.noprime","text":"noprime[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)\nnoprime[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)\n\nApply noprime to the site indices that are shared by M1 and M2.\n\nReturns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.noprime-Tuple{typeof(siteinds), typeof(uniqueinds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.noprime","text":"noprime[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)\n\nApply noprime to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.\n\nThe ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.addtags-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.addtags","text":"addtags[!](M::MPS, args...; kwargs...)\naddtags[!](M::MPO, args...; kwargs...)\n\nApply addtags to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.addtags-Tuple{typeof(siteinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.addtags","text":"addtags[!](siteinds, M::MPS, args...; kwargs...)\naddtags[!](siteinds, M::MPO, args...; kwargs...)\n\nApply addtags to all site indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.addtags-Tuple{typeof(linkinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.addtags","text":"addtags[!](linkinds, M::MPS, args...; kwargs...)\naddtags[!](linkinds, M::MPO, args...; kwargs...)\n\nApply addtags to all link indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.addtags-Tuple{typeof(siteinds), typeof(commoninds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.addtags","text":"addtags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)\naddtags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)\n\nApply addtags to the site indices that are shared by M1 and M2.\n\nReturns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.addtags-Tuple{typeof(siteinds), typeof(uniqueinds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.addtags","text":"addtags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)\n\nApply addtags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.\n\nThe ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.removetags-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.removetags","text":"removetags[!](M::MPS, args...; kwargs...)\nremovetags[!](M::MPO, args...; kwargs...)\n\nApply removetags to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.removetags-Tuple{typeof(siteinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.removetags","text":"removetags[!](siteinds, M::MPS, args...; kwargs...)\nremovetags[!](siteinds, M::MPO, args...; kwargs...)\n\nApply removetags to all site indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.removetags-Tuple{typeof(linkinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.removetags","text":"removetags[!](linkinds, M::MPS, args...; kwargs...)\nremovetags[!](linkinds, M::MPO, args...; kwargs...)\n\nApply removetags to all link indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.removetags-Tuple{typeof(siteinds), typeof(commoninds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.removetags","text":"removetags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)\nremovetags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)\n\nApply removetags to the site indices that are shared by M1 and M2.\n\nReturns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.removetags-Tuple{typeof(siteinds), typeof(uniqueinds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.removetags","text":"removetags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)\n\nApply removetags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.\n\nThe ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.replacetags-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.replacetags","text":"replacetags[!](M::MPS, args...; kwargs...)\nreplacetags[!](M::MPO, args...; kwargs...)\n\nApply replacetags to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.replacetags-Tuple{typeof(siteinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.replacetags","text":"replacetags[!](siteinds, M::MPS, args...; kwargs...)\nreplacetags[!](siteinds, M::MPO, args...; kwargs...)\n\nApply replacetags to all site indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.replacetags-Tuple{typeof(linkinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.replacetags","text":"replacetags[!](linkinds, M::MPS, args...; kwargs...)\nreplacetags[!](linkinds, M::MPO, args...; kwargs...)\n\nApply replacetags to all link indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.replacetags-Tuple{typeof(siteinds), typeof(commoninds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.replacetags","text":"replacetags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)\nreplacetags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)\n\nApply replacetags to the site indices that are shared by M1 and M2.\n\nReturns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.TagSets.replacetags-Tuple{typeof(siteinds), typeof(uniqueinds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.TagSets.replacetags","text":"replacetags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)\n\nApply replacetags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.\n\nThe ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.settags-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.settags","text":"settags[!](M::MPS, args...; kwargs...)\nsettags[!](M::MPO, args...; kwargs...)\n\nApply settags to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.settags-Tuple{typeof(siteinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.settags","text":"settags[!](siteinds, M::MPS, args...; kwargs...)\nsettags[!](siteinds, M::MPO, args...; kwargs...)\n\nApply settags to all site indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.settags-Tuple{typeof(linkinds), AbstractMPS}","page":"MPS and MPO","title":"ITensors.settags","text":"settags[!](linkinds, M::MPS, args...; kwargs...)\nsettags[!](linkinds, M::MPO, args...; kwargs...)\n\nApply settags to all link indices of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.settags-Tuple{typeof(siteinds), typeof(commoninds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.settags","text":"settags[!](siteinds, commoninds, M1::MPO, M2::MPS, args...; kwargs...)\nsettags[!](siteinds, commoninds, M1::MPO, M2::MPO, args...; kwargs...)\n\nApply settags to the site indices that are shared by M1 and M2.\n\nReturns new MPSs/MPOs. The ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.settags-Tuple{typeof(siteinds), typeof(uniqueinds), AbstractMPS, AbstractMPS}","page":"MPS and MPO","title":"ITensors.settags","text":"settags[!](siteinds, uniqueinds, M1::MPO, M2::MPS, args...; kwargs...)\n\nApply settags to the site indices of M1 that are not shared with M2. Returns new MPSs/MPOs.\n\nThe ITensors of the MPSs/MPOs will be a view of the storage of the original ITensors.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Operations","page":"MPS and MPO","title":"Operations","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"expect(::MPS, ::Any)\ncorrelation_matrix(::MPS, ::AbstractString, ::AbstractString)\ndag(::ITensors.AbstractMPS)\ndense(::ITensors.AbstractMPS)\nmovesite(::ITensors.AbstractMPS, ::Pair{Int, Int};orthocenter::Int,kwargs...)\northogonalize!\nreplacebond!(::MPS, ::Int, ::ITensor)\nsample(::MPS)\nsample!(::MPS)\nsample(::MPO)\nswapbondsites(::ITensors.AbstractMPS, ::Int; kwargs...)\ntruncate!","category":"page"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.expect-Tuple{MPS, Any}","page":"MPS and MPO","title":"ITensors.ITensorMPS.expect","text":"expect(psi::MPS, op::AbstractString...; kwargs...)\nexpect(psi::MPS, op::Matrix{<:Number}...; kwargs...)\nexpect(psi::MPS, ops; kwargs...)\n\nGiven an MPS psi and a single operator name, returns a vector of the expected value of the operator on each site of the MPS.\n\nIf multiple operator names are provided, returns a tuple of expectation value vectors.\n\nIf a container of operator names is provided, returns the same type of container with names replaced by vectors of expectation values.\n\nOptional Keyword Arguments\n\nsites = 1:length(psi): compute expected values only for sites in the given range\n\nExamples\n\nN = 10\n\ns = siteinds(\"S=1/2\", N)\npsi = randomMPS(s; linkdims=8)\nZ = expect(psi, \"Sz\") # compute for all sites\nZ = expect(psi, \"Sz\"; sites=2:4) # compute for sites 2,3,4\nZ3 = expect(psi, \"Sz\"; sites=3) # compute for site 3 only (output will be a scalar)\nXZ = expect(psi, [\"Sx\", \"Sz\"]) # compute Sx and Sz for all sites\nZ = expect(psi, [1/2 0; 0 -1/2]) # same as expect(psi,\"Sz\")\n\ns = siteinds(\"Electron\", N)\npsi = randomMPS(s; linkdims=8)\ndens = expect(psi, \"Ntot\")\nupdens, dndens = expect(psi, \"Nup\", \"Ndn\") # pass more than one operator\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.correlation_matrix-Tuple{MPS, AbstractString, AbstractString}","page":"MPS and MPO","title":"ITensors.ITensorMPS.correlation_matrix","text":"correlation_matrix(psi::MPS,\n Op1::AbstractString,\n Op2::AbstractString;\n kwargs...)\n\ncorrelation_matrix(psi::MPS,\n Op1::Matrix{<:Number},\n Op2::Matrix{<:Number};\n kwargs...)\n\nGiven an MPS psi and two strings denoting operators (as recognized by the op function), computes the two-point correlation function matrix C[i,j] = using efficient MPS techniques. Returns the matrix C.\n\nOptional Keyword Arguments\n\nsites = 1:length(psi): compute correlations only for sites in the given range\nishermitian = false : if false, force independent calculations of the matrix elements above and below the diagonal, while if true assume they are complex conjugates.\n\nFor a correlation matrix of size NxN and an MPS of typical bond dimension m, the scaling of this algorithm is N^2*m^3.\n\nExamples\n\nN = 30\nm = 4\n\ns = siteinds(\"S=1/2\", N)\npsi = randomMPS(s; linkdims=m)\nCzz = correlation_matrix(psi, \"Sz\", \"Sz\")\nCzz = correlation_matrix(psi, [1/2 0; 0 -1/2], [1/2 0; 0 -1/2]) # same as above\n\ns = siteinds(\"Electron\", N; conserve_qns=true)\npsi = randomMPS(s, n -> isodd(n) ? \"Up\" : \"Dn\"; linkdims=m)\nCuu = correlation_matrix(psi, \"Cdagup\", \"Cup\"; sites=2:8)\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.dag-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.dag","text":"dag[!](M::MPS, args...; kwargs...)\ndag[!](M::MPO, args...; kwargs...)\n\nApply dag to all ITensors of an MPS/MPO, returning a new MPS/MPO.\n\nThe ITensors of the MPS/MPO will be a view of the storage of the original ITensors. Alternatively apply the function in-place.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#NDTensors.dense-Tuple{AbstractMPS}","page":"MPS and MPO","title":"NDTensors.dense","text":"dense(::MPS/MPO)\n\nGiven an MPS (or MPO), return a new MPS (or MPO) having called dense on each ITensor to convert each tensor to use dense storage and remove any QN or other sparse structure information, if it is not dense already.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.movesite-Tuple{AbstractMPS, Pair{Int64, Int64}}","page":"MPS and MPO","title":"ITensors.ITensorMPS.movesite","text":"movesite(::Union{MPS, MPO}, n1n2::Pair{Int, Int})\n\nCreate a new MPS/MPO where the site at n1 is moved to n2, for a pair n1n2 = n1 => n2.\n\nThis is done with a series a pairwise swaps, and can introduce a lot of entanglement into your state, so use with caution.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.orthogonalize!","page":"MPS and MPO","title":"ITensors.ITensorMPS.orthogonalize!","text":"orthogonalize!(M::MPS, j::Int; kwargs...)\northogonalize(M::MPS, j::Int; kwargs...)\n\northogonalize!(M::MPO, j::Int; kwargs...)\northogonalize(M::MPO, j::Int; kwargs...)\n\nMove the orthogonality center of the MPS to site j. No observable property of the MPS will be changed, and no truncation of the bond indices is performed. Afterward, tensors 1,2,...,j-1 will be left-orthogonal and tensors j+1,j+2,...,N will be right-orthogonal.\n\nEither modify in-place with orthogonalize! or out-of-place with orthogonalize.\n\n\n\n\n\n","category":"function"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.replacebond!-Tuple{MPS, Int64, ITensor}","page":"MPS and MPO","title":"ITensors.ITensorMPS.replacebond!","text":"replacebond!(M::MPS, b::Int, phi::ITensor; kwargs...)\n\nFactorize the ITensor phi and replace the ITensors b and b+1 of MPS M with the factors. Choose the orthogonality with ortho=\"left\"/\"right\".\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.sample-Tuple{MPS}","page":"MPS and MPO","title":"ITensors.ITensorMPS.sample","text":"sample(m::MPS)\n\nGiven a normalized MPS m with orthocenter(m)==1, returns a Vector{Int} of length(m) corresponding to one sample of the probability distribution defined by squaring the components of the tensor that the MPS represents\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.sample!-Tuple{MPS}","page":"MPS and MPO","title":"ITensors.ITensorMPS.sample!","text":"sample!(m::MPS)\n\nGiven a normalized MPS m, returns a Vector{Int} of length(m) corresponding to one sample of the probability distribution defined by squaring the components of the tensor that the MPS represents. If the MPS does not have an orthogonality center, orthogonalize!(m,1) will be called before computing the sample.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.sample-Tuple{MPO}","page":"MPS and MPO","title":"ITensors.ITensorMPS.sample","text":"sample(M::MPO)\n\nGiven a normalized MPO M, returns a Vector{Int} of length(M) corresponding to one sample of the probability distribution defined by the MPO, treating the MPO as a density matrix.\n\nThe MPO M should have an (approximately) positive spectrum.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.swapbondsites-Tuple{AbstractMPS, Int64}","page":"MPS and MPO","title":"ITensors.ITensorMPS.swapbondsites","text":"swapbondsites(ψ::Union{MPS, MPO}, b::Integer; kwargs...)\n\nSwap the sites b and b+1.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#NDTensors.truncate!","page":"MPS and MPO","title":"NDTensors.truncate!","text":"truncate!(M::MPS; kwargs...)\ntruncate!(M::MPO; kwargs...)\n\nPerform a truncation of all bonds of an MPS/MPO, using the truncation parameters (cutoff,maxdim, etc.) provided as keyword arguments.\n\nKeyword arguments:\n\nsite_range=1:N - only truncate the MPS bonds between these sites\n\n\n\n\n\n","category":"function"},{"location":"MPSandMPO.html#Gate-evolution","page":"MPS and MPO","title":"Gate evolution","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"product(::ITensor, ::ITensors.AbstractMPS)\nproduct(::Vector{ITensor}, ::ITensors.AbstractMPS)","category":"page"},{"location":"MPSandMPO.html#ITensors.product-Tuple{ITensor, AbstractMPS}","page":"MPS and MPO","title":"ITensors.product","text":"apply(o::ITensor, ψ::Union{MPS, MPO}, [ns::Vector{Int}]; kwargs...)\nproduct([...])\n\nGet the product of the operator o with the MPS/MPO ψ, where the operator is applied to the sites ns. If ns are not specified, the sites are determined by the common indices between o and the site indices of ψ.\n\nIf ns are non-contiguous, the sites of the MPS are moved to be contiguous. By default, the sites are moved back to their original locations. You can leave them where they are by setting the keyword argument move_sites_back to false.\n\nKeywords\n\ncutoff::Real: singular value truncation cutoff.\nmaxdim::Int: maximum MPS/MPO dimension.\napply_dag::Bool = false: apply the gate and the dagger of the gate (only relevant for MPO evolution).\nmove_sites_back::Bool = true: after the ITensors are applied to the MPS or MPO, move the sites of the MPS or MPO back to their original locations.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.product-Tuple{Vector{ITensor}, AbstractMPS}","page":"MPS and MPO","title":"ITensors.product","text":"apply(As::Vector{<:ITensor}, M::Union{MPS, MPO}; kwargs...)\nproduct([...])\n\nApply the ITensors As to the MPS or MPO M, treating them as gates or matrices from pairs of prime or unprimed indices.\n\nKeywords\n\ncutoff::Real: singular value truncation cutoff.\nmaxdim::Int: maximum MPS/MPO dimension.\napply_dag::Bool = false: apply the gate and the dagger of the gate (only relevant for MPO evolution).\nmove_sites_back::Bool = true: after the ITensor is applied to the MPS or MPO, move the sites of the MPS or MPO back to their original locations.\n\nExamples\n\nApply one-site gates to an MPS:\n\nN = 3\n\nITensors.op(::OpName\"σx\", ::SiteType\"S=1/2\", s::Index) =\n 2*op(\"Sx\", s)\n\nITensors.op(::OpName\"σz\", ::SiteType\"S=1/2\", s::Index) =\n 2*op(\"Sz\", s)\n\n# Make the operator list.\nos = [(\"σx\", n) for n in 1:N]\nappend!(os, [(\"σz\", n) for n in 1:N])\n\n@show os\n\ns = siteinds(\"S=1/2\", N)\ngates = ops(os, s)\n\n# Starting state |↑↑↑⟩\nψ0 = MPS(s, \"↑\")\n\n# Apply the gates.\nψ = apply(gates, ψ0; cutoff = 1e-15)\n\n# Test against exact (full) wavefunction\nprodψ = apply(gates, prod(ψ0))\n@show prod(ψ) ≈ prodψ\n\n# The result is:\n# σz₃ σz₂ σz₁ σx₃ σx₂ σx₁ |↑↑↑⟩ = -|↓↓↓⟩\n@show inner(ψ, MPS(s, \"↓\")) == -1\n\nApply nonlocal two-site gates and one-site gates to an MPS:\n\n# 2-site gate\nfunction ITensors.op(::OpName\"CX\", ::SiteType\"S=1/2\", s1::Index, s2::Index)\n mat = [1 0 0 0\n 0 1 0 0\n 0 0 0 1\n 0 0 1 0]\n return itensor(mat, s2', s1', s2, s1)\nend\n\nos = [(\"CX\", 1, 3), (\"σz\", 3)]\n\n@show os\n\n# Start with the state |↓↑↑⟩\nψ0 = MPS(s, n -> n == 1 ? \"↓\" : \"↑\")\n\n# The result is:\n# σz₃ CX₁₃ |↓↑↑⟩ = -|↓↑↓⟩\nψ = apply(ops(os, s), ψ0; cutoff = 1e-15)\n@show inner(ψ, MPS(s, n -> n == 1 || n == 3 ? \"↓\" : \"↑\")) == -1\n\nPerform TEBD-like time evolution:\n\n# Define the nearest neighbor term `S⋅S` for the Heisenberg model\nfunction ITensors.op(::OpName\"expS⋅S\", ::SiteType\"S=1/2\",\n s1::Index, s2::Index; τ::Number)\n O = 0.5 * op(\"S+\", s1) * op(\"S-\", s2) +\n 0.5 * op(\"S-\", s1) * op(\"S+\", s2) +\n op(\"Sz\", s1) * op(\"Sz\", s2)\n return exp(τ * O)\nend\n\nτ = -0.1im\nos = [(\"expS⋅S\", (1, 2), (τ = τ,)),\n (\"expS⋅S\", (2, 3), (τ = τ,))]\nψ0 = MPS(s, n -> n == 1 ? \"↓\" : \"↑\")\nexpτH = ops(os, s)\nψτ = apply(expτH, ψ0)\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Algebra-Operations","page":"MPS and MPO","title":"Algebra Operations","text":"","category":"section"},{"location":"MPSandMPO.html","page":"MPS and MPO","title":"MPS and MPO","text":"inner(::MPST, ::MPST) where {MPST <: ITensors.AbstractMPS}\ndot(::MPST, ::MPST) where {MPST <: ITensors.AbstractMPS}\nloginner(::MPST, ::MPST) where {MPST <: ITensors.AbstractMPS}\nlogdot(::MPST, ::MPST) where {MPST <: ITensors.AbstractMPS}\ninner(::MPS, ::MPO, ::MPS)\ndot(::MPS, ::MPO, ::MPS)\ninner(::MPO, ::MPS, ::MPO, ::MPS)\ndot(::MPO, ::MPS, ::MPO, ::MPS)\nnorm(::ITensors.AbstractMPS)\nnormalize(::ITensors.AbstractMPS)\nnormalize!(::ITensors.AbstractMPS)\nlognorm(::ITensors.AbstractMPS)\n+(::ITensors.AbstractMPS...)\ncontract(::MPO, ::MPS)\napply(::MPO, ::MPS)\ncontract(::MPO, ::MPO)\napply(::MPO, ::MPO)\nerror_contract(y::MPS, A::MPO, x::MPS)\nouter(::MPS, ::MPS)\nprojector(::MPS)","category":"page"},{"location":"MPSandMPO.html#ITensors.inner-Union{Tuple{MPST}, Tuple{MPST, MPST}} where MPST<:AbstractMPS","page":"MPS and MPO","title":"ITensors.inner","text":"inner(A::MPS, B::MPS)\ninner(A::MPO, B::MPO)\n\nCompute the inner product ⟨A|B⟩. If A and B are MPOs, computes the Frobenius inner product.\n\nUse loginner to avoid underflow/overflow for taking overlaps of large MPS or MPO.\n\ncompat: ITensors 0.3\n\n\nBefore ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.\n\nSame as dot.\n\nSee also loginner, logdot.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#LinearAlgebra.dot-Union{Tuple{MPST}, Tuple{MPST, MPST}} where MPST<:AbstractMPS","page":"MPS and MPO","title":"LinearAlgebra.dot","text":"dot(A::MPS, B::MPS)\ndot(A::MPO, B::MPO)\n\nSame as inner.\n\nSee also loginner, logdot.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.loginner-Union{Tuple{MPST}, Tuple{MPST, MPST}} where MPST<:AbstractMPS","page":"MPS and MPO","title":"ITensors.ITensorMPS.loginner","text":"loginner(A::MPS, B::MPS)\nloginner(A::MPO, B::MPO)\n\nCompute the logarithm of the inner product ⟨A|B⟩. If A and B are MPOs, computes the logarithm of the Frobenius inner product.\n\nThis is useful for larger MPS/MPO, where in the limit of large numbers of sites the inner product can diverge or approach zero.\n\ncompat: ITensors 0.3\n\n\nBefore ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.\n\nSame as logdot.\n\nSee also inner, dot.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.logdot-Union{Tuple{MPST}, Tuple{MPST, MPST}} where MPST<:AbstractMPS","page":"MPS and MPO","title":"ITensors.ITensorMPS.logdot","text":"logdot(A::MPS, B::MPS)\nlogdot(A::MPO, B::MPO)\n\nSame as loginner.\n\nSee also inner, dot.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.inner-Tuple{MPS, MPO, MPS}","page":"MPS and MPO","title":"ITensors.inner","text":"inner(y::MPS, A::MPO, x::MPS)\n\nCompute ⟨y|A|x⟩ = ⟨y|Ax⟩ efficiently and exactly without making any intermediate MPOs. In general it is more efficient and accurate than inner(y, apply(A, x)).\n\nThis is helpful for computing the expectation value of an operator A, which would be:\n\ninner(x', A, x)\n\nassuming x is normalized.\n\nIf you want to compute ⟨By|Ax⟩ you can use inner(B::MPO, y::MPS, A::MPO, x::MPS).\n\nThis is helpful for computing the variance of an operator A, which would be:\n\ninner(A, x, A, x) - inner(x', A, x) ^ 2\n\nassuming x is normalized.\n\ncompat: ITensors 0.3\n\n\nBefore ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.\n\nSame as dot.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#LinearAlgebra.dot-Tuple{MPS, MPO, MPS}","page":"MPS and MPO","title":"LinearAlgebra.dot","text":"dot(y::MPS, A::MPO, x::MPS)\n\nSame as inner.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.inner-Tuple{MPO, MPS, MPO, MPS}","page":"MPS and MPO","title":"ITensors.inner","text":"inner(B::MPO, y::MPS, A::MPO, x::MPS)\n\nCompute ⟨By|A|x⟩ = ⟨By|Ax⟩ efficiently and exactly without making any intermediate MPOs. In general it is more efficient and accurate than inner(apply(B, y), apply(A, x)).\n\nThis is helpful for computing the variance of an operator A, which would be:\n\ninner(A, x, A, x) - inner(x, A, x) ^ 2\n\ncompat: ITensors 0.3\n\n\nBefore ITensors 0.3, inner had a keyword argument make_inds_match that default to true. When true, the function attempted to make the site indices match before contracting. So for example, the inputs could have different site indices, as long as they have the same dimensions or QN blocks. This behavior was fragile since it only worked for MPS with single site indices per tensor, and as of ITensors 0.3 has been deprecated. As of ITensors 0.3 you will need to make sure the MPS or MPO you input have compatible site indices to contract over, such as by making sure the prime levels match properly.\n\nSame as dot.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#LinearAlgebra.dot-Tuple{MPO, MPS, MPO, MPS}","page":"MPS and MPO","title":"LinearAlgebra.dot","text":"dot(B::MPO, y::MPS, A::MPO, x::MPS)\n\nSame as inner.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#LinearAlgebra.norm-Tuple{AbstractMPS}","page":"MPS and MPO","title":"LinearAlgebra.norm","text":"norm(A::MPS)\nnorm(A::MPO)\n\nCompute the norm of the MPS or MPO.\n\nIf the MPS or MPO has a well defined orthogonality center, this reduces to the norm of the orthogonality center tensor. Otherwise, it computes the norm with the full inner product of the MPS/MPO with itself.\n\nSee also lognorm.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#LinearAlgebra.normalize-Tuple{AbstractMPS}","page":"MPS and MPO","title":"LinearAlgebra.normalize","text":"normalize(A::MPS; (lognorm!)=[])\nnormalize(A::MPO; (lognorm!)=[])\n\nReturn a new MPS or MPO A that is the same as the original MPS or MPO but with norm(A) ≈ 1.\n\nIn practice, this evenly spreads lognorm(A) over the tensors within the range of the orthogonality center to avoid numerical overflow in the case of diverging norms.\n\nSee also normalize!, norm, lognorm.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#LinearAlgebra.normalize!-Tuple{AbstractMPS}","page":"MPS and MPO","title":"LinearAlgebra.normalize!","text":"normalize!(A::MPS; (lognorm!)=[])\nnormalize!(A::MPO; (lognorm!)=[])\n\nChange the MPS or MPO A in-place such that norm(A) ≈ 1. This modifies the data of the tensors within the orthogonality center.\n\nIn practice, this evenly spreads lognorm(A) over the tensors within the range of the orthogonality center to avoid numerical overflow in the case of diverging norms.\n\nIf the norm of the input MPS or MPO is 0, normalizing is ill-defined. In this case, we just return the original MPS or MPO. You can check for this case as follows:\n\ns = siteinds(\"S=1/2\", 4)\nψ = 0 * randomMPS(s)\nlognorm_ψ = []\nnormalize!(ψ; (lognorm!)=lognorm_ψ)\nlognorm_ψ[1] == -Inf # There was an infinite norm\n\nSee also normalize, norm, lognorm.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.lognorm-Tuple{AbstractMPS}","page":"MPS and MPO","title":"ITensors.ITensorMPS.lognorm","text":"lognorm(A::MPS)\nlognorm(A::MPO)\n\nCompute the logarithm of the norm of the MPS or MPO.\n\nThis is useful for larger MPS/MPO that are not gauged, where in the limit of large numbers of sites the norm can diverge or approach zero.\n\nSee also norm, logdot.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#Base.:+-Tuple{Vararg{AbstractMPS}}","page":"MPS and MPO","title":"Base.:+","text":"+(A::MPS/MPO...; kwargs...)\nadd(A::MPS/MPO...; kwargs...)\n\nAdd arbitrary numbers of MPS/MPO with each other, optionally truncating the results.\n\nA cutoff of 1e-15 is used by default, and in general users should set their own cutoff for their particular application.\n\nKeywords\n\ncutoff::Real: singular value truncation cutoff\nmaxdim::Int: maximum MPS/MPO bond dimension\nalg = \"densitymatrix\": \"densitymatrix\" or \"directsum\". \"densitymatrix\" adds the MPS/MPO by adding up and diagoanlizing local density matrices site by site in a single sweep through the system, truncating the density matrix with cutoff and maxdim. \"directsum\" performs a direct sum of each tensors on each site of the input MPS/MPO being summed. It doesn't perform any truncation, and therefore ignores cutoff and maxdim. The bond dimension of the output is the sum of the bond dimensions of the inputs. You can truncate the resulting MPS/MPO with the truncate! function.\n\nExamples\n\nN = 10\n\ns = siteinds(\"S=1/2\", N; conserve_qns = true)\n\nstate = n -> isodd(n) ? \"↑\" : \"↓\"\nψ₁ = randomMPS(s, state, 2)\nψ₂ = randomMPS(s, state, 2)\nψ₃ = randomMPS(s, state, 2)\n\nψ = +(ψ₁, ψ₂; cutoff = 1e-8)\n\n# Can use:\n#\n# ψ = ψ₁ + ψ₂\n#\n# but generally you want to set a custom `cutoff` and `maxdim`.\n\nprintln()\n@show inner(ψ, ψ)\n@show inner(ψ₁, ψ₂) + inner(ψ₁, ψ₂) + inner(ψ₂, ψ₁) + inner(ψ₂, ψ₂)\n\n# Computes ψ₁ + 2ψ₂\nψ = ψ₁ + 2ψ₂\n\nprintln()\n@show inner(ψ, ψ)\n@show inner(ψ₁, ψ₁) + 2 * inner(ψ₁, ψ₂) + 2 * inner(ψ₂, ψ₁) + 4 * inner(ψ₂, ψ₂)\n\n# Computes ψ₁ + 2ψ₂ + ψ₃\nψ = ψ₁ + 2ψ₂ + ψ₃\n\nprintln()\n@show inner(ψ, ψ)\n@show inner(ψ₁, ψ₁) + 2 * inner(ψ₁, ψ₂) + inner(ψ₁, ψ₃) +\n 2 * inner(ψ₂, ψ₁) + 4 * inner(ψ₂, ψ₂) + 2 * inner(ψ₂, ψ₃) +\n inner(ψ₃, ψ₁) + 2 * inner(ψ₃, ψ₂) + inner(ψ₃, ψ₃)\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#NDTensors.contract-Tuple{MPO, MPS}","page":"MPS and MPO","title":"NDTensors.contract","text":"contract(ψ::MPS, A::MPO; kwargs...) -> MPS\n*(::MPS, ::MPO; kwargs...) -> MPS\n\ncontract(A::MPO, ψ::MPS; kwargs...) -> MPS\n*(::MPO, ::MPS; kwargs...) -> MPS\n\nContract the MPO A with the MPS ψ, returning an MPS with the unique site indices of the MPO.\n\nFor example, for an MPO with site indices with prime levels of 1 and 0, such as -s'-A-s-, and an MPS with site indices with prime levels of 0, such as -s-x, the result is an MPS y with site indices with prime levels of 1, -s'-y = -s'-A-s-x.\n\nSince it is common to contract an MPO with prime levels of 1 and 0 with an MPS with prime level of 0 and want a resulting MPS with prime levels of 0, we provide a convenience function apply:\n\napply(A, x; kwargs...) = replaceprime(contract(A, x; kwargs...), 2 => 1)`.\n\nChoose the method with the method keyword, for example \"densitymatrix\" and \"naive\".\n\nKeywords\n\ncutoff::Float64=1e-13: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.\nmaxdim::Int=maxlinkdim(A) * maxlinkdim(ψ)): the maximal bond dimension of the results MPS.\nmindim::Int=1: the minimal bond dimension of the resulting MPS.\nnormalize::Bool=false: whether or not to normalize the resulting MPS.\nmethod::String=\"densitymatrix\": the algorithm to use for the contraction. Currently the options are \"densitymatrix\", where the network formed by the MPO and MPS is squared and contracted down to a density matrix which is diagonalized iteratively at each site, and \"naive\", where the MPO and MPS tensor are contracted exactly at each site and then a truncation of the resulting MPS is performed.\n\nSee also apply.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.apply-Tuple{MPO, MPS}","page":"MPS and MPO","title":"ITensors.apply","text":"apply(A::MPO, x::MPS; kwargs...)\n\nContract the MPO A with the MPS x and then map the prime level of the resulting MPS back to 0.\n\nEquivalent to replaceprime(contract(A, x; kwargs...), 2 => 1).\n\nSee also contract for details about the arguments available.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#NDTensors.contract-Tuple{MPO, MPO}","page":"MPS and MPO","title":"NDTensors.contract","text":"contract(A::MPO, B::MPO; kwargs...) -> MPO\n*(::MPO, ::MPO; kwargs...) -> MPO\n\nContract the MPO A with the MPO B, returning an MPO with the site indices that are not shared between A and B.\n\nIf you are contracting two MPOs with the same sets of indices, likely you want to call something like:\n\nC = contract(A', B; cutoff=1e-12)\nC = replaceprime(C, 2 => 1)\n\nThat is because if MPO A has the index structure -s'-A-s- and MPO B has the Index structure -s'-B-s-, if we only want to contract over on set of the indices, we would do (-s'-A-s-)'-s'-B-s- = -s''-A-s'-s'-B-s- = -s''-C-s-, and then map the prime levels back to pairs of primed and unprimed indices with: replaceprime(-s''-C-s-, 2 => 1) = -s'-C-s-.\n\nSince this is a common use case, you can use the convenience function:\n\nC = apply(A, B; cutoff=1e-12)\n\nwhich is the same as the code above.\n\nIf you are contracting MPOs that have diverging norms, such as MPOs representing sums of local operators, the truncation can become numerically unstable (see https://arxiv.org/abs/1909.06341 for a more numerically stable alternative). For now, you can use the following options to contract MPOs like that:\n\nC = contract(A, B; alg=\"naive\", truncate=false)\n# Bring the indices back to pairs of primed and unprimed\nC = apply(A, B; alg=\"naive\", truncate=false)\n\nKeywords\n\ncutoff::Float64=1e-14: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.\nmaxdim::Int=maxlinkdim(A) * maxlinkdim(B)): the maximal bond dimension of the results MPS.\nmindim::Int=1: the minimal bond dimension of the resulting MPS.\nalg=\"zipup\": Either \"zipup\" or \"naive\". \"zipup\" contracts pairs of site tensors and truncates with SVDs in a sweep across the sites, while \"naive\" first contracts pairs of tensor exactly and then truncates at the end if truncate=true.\ntruncate=true: Enable or disable truncation. If truncate=false, ignore other truncation parameters like cutoff and maxdim. This is most relevant for the \"naive\" version, if you just want to contract the tensors pairwise exactly. This can be useful if you are contracting MPOs that have diverging norms, such as MPOs originating from sums of local operators.\n\nSee also apply for details about the arguments available.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.apply-Tuple{MPO, MPO}","page":"MPS and MPO","title":"ITensors.apply","text":"apply(A::MPO, B::MPO; kwargs...)\n\nContract the MPO A' with the MPO B and then map the prime level of the resulting MPO back to having pairs of indices with prime levels of 1 and 0.\n\nEquivalent to replaceprime(contract(A', B; kwargs...), 2 => 1).\n\nSee also contract for details about the arguments available.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.error_contract-Tuple{MPS, MPO, MPS}","page":"MPS and MPO","title":"ITensors.ITensorMPS.error_contract","text":"error_contract(y::MPS, A::MPO, x::MPS;\n make_inds_match::Bool = true)\nerror_contract(y::MPS, x::MPS, x::MPO;\n make_inds_match::Bool = true)\n\nCompute the distance between A|x> and an approximation MPS y: | |y> - A|x> |/| A|x> | = √(1 + ( - 2*real())/).\n\nIf make_inds_match = true, the function attempts match the site indices of y with the site indices of A that are not common with x.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#NDTensors.outer-Tuple{MPS, MPS}","page":"MPS and MPO","title":"NDTensors.outer","text":"outer(x::MPS, y::MPS; ) -> MPO\n\nCompute the outer product of MPS x and MPS y, returning an MPO approximation. Note that y will be conjugated.\n\nIn Dirac notation, this is the operation |x⟩⟨y|.\n\nIf you want an outer product of an MPS with itself, you should call outer(x', x; kwargs...) so that the resulting MPO has site indices with indices coming in pairs of prime levels of 1 and 0. If not, the site indices won't be unique which would not be an outer product.\n\nFor example:\n\ns = siteinds(\"S=1/2\", 5)\nx = randomMPS(s)\ny = randomMPS(s)\nouter(x, y) # Incorrect! Site indices must be unique.\nouter(x', y) # Results in an MPO with pairs of primed and unprimed indices.\n\nThis allows for more general outer products, such as more general MPO outputs which don't have pairs of primed and unprimed indices, or outer products where the input MPS are vectorizations of MPOs.\n\nFor example:\n\ns = siteinds(\"S=1/2\", 5)\nX = MPO(s, \"Id\")\nY = MPO(s, \"Id\")\nx = convert(MPS, X)\ny = convert(MPS, Y)\nouter(x, y) # Incorrect! Site indices must be unique.\nouter(x', y) # Incorrect! Site indices must be unique.\nouter(addtags(x, \"Out\"), addtags(y, \"In\")) # This performs a proper outer product.\n\nThe keyword arguments determine the truncation, and accept the same arguments as contract(::MPO, ::MPO; kwargs...).\n\nSee also apply, contract.\n\n\n\n\n\n","category":"method"},{"location":"MPSandMPO.html#ITensors.ITensorMPS.projector-Tuple{MPS}","page":"MPS and MPO","title":"ITensors.ITensorMPS.projector","text":"projector(x::MPS; ) -> MPO\n\nComputes the projector onto the state x. In Dirac notation, this is the operation |x⟩⟨x|/|⟨x|x⟩|².\n\nUse keyword arguments to control the level of truncation, which are the same as those accepted by contract(::MPO, ::MPO; kw...).\n\nKeywords\n\nnormalize::Bool=true: whether or not to normalize the input MPS before forming the projector. If normalize==false and the input MPS is not already normalized, this function will not output a proper project, and simply outputs outer(x, x) = |x⟩⟨x|, i.e. the projector scaled by norm(x)^2.\ntruncation keyword arguments accepted by contract(::MPO, ::MPO; kw...).\n\nSee also outer, contract.\n\n\n\n\n\n","category":"method"},{"location":"ProjMPO.html#ProjMPO","page":"ProjMPO","title":"ProjMPO","text":"","category":"section"},{"location":"ProjMPO.html#Description","page":"ProjMPO","title":"Description","text":"","category":"section"},{"location":"ProjMPO.html","page":"ProjMPO","title":"ProjMPO","text":"ProjMPO","category":"page"},{"location":"ProjMPO.html#ITensors.ITensorMPS.ProjMPO","page":"ProjMPO","title":"ITensors.ITensorMPS.ProjMPO","text":"A ProjMPO computes and stores the projection of an MPO into a basis defined by an MPS, leaving a certain number of site indices of the MPO unprojected. Which sites are unprojected can be shifted by calling the position! method.\n\nDrawing of the network represented by a ProjMPO P(H), showing the case of nsite(P)==2 and position!(P,psi,4) for an MPS psi:\n\no--o--o- -o--o--o--o--o--o \n\n\n\n\n\n","category":"type"},{"location":"ProjMPO.html#Methods","page":"ProjMPO","title":"Methods","text":"","category":"section"},{"location":"ProjMPO.html","page":"ProjMPO","title":"ProjMPO","text":"product(::ProjMPO,::ITensor)\nposition!(::ProjMPO, ::MPS, ::Int)\nnoiseterm(::ProjMPO,::ITensor,::String)","category":"page"},{"location":"ProjMPO.html#ITensors.product-Tuple{ProjMPO, ITensor}","page":"ProjMPO","title":"ITensors.product","text":"product(P::ProjMPO,v::ITensor)::ITensor\n\n(P::ProjMPO)(v::ITensor)\n\nEfficiently multiply the ProjMPO P by an ITensor v in the sense that the ProjMPO is a generalized square matrix or linear operator and v is a generalized vector in the space where it acts. The returned ITensor will have the same indices as v. The operator overload P(v) is shorthand for product(P,v).\n\n\n\n\n\n","category":"method"},{"location":"ProjMPO.html#ITensors.ITensorMPS.position!-Tuple{ProjMPO, MPS, Int64}","page":"ProjMPO","title":"ITensors.ITensorMPS.position!","text":"position!(P::ProjMPO, psi::MPS, pos::Int)\n\nGiven an MPS psi, shift the projection of the MPO represented by the ProjMPO P such that the set of unprojected sites begins with site pos. This operation efficiently reuses previous projections of the MPO on sites that have already been projected. The MPS psi must have compatible bond indices with the previous projected MPO tensors for this operation to succeed.\n\n\n\n\n\n","category":"method"},{"location":"ProjMPO.html#ITensors.ITensorMPS.noiseterm-Tuple{ProjMPO, ITensor, String}","page":"ProjMPO","title":"ITensors.ITensorMPS.noiseterm","text":"noiseterm(P::ProjMPO,\n phi::ITensor,\n ortho::String)\n\nReturn a \"noise term\" or density matrix perturbation ITensor as proposed in Phys. Rev. B 72, 180403 for aiding convergence of DMRG calculations. The ITensor phi is the contracted product of MPS tensors acted on by the ProjMPO P, and ortho is a String which can take the values \"left\" or \"right\" depending on the sweeping direction of the DMRG calculation.\n\n\n\n\n\n","category":"method"},{"location":"ProjMPO.html#Properties","page":"ProjMPO","title":"Properties","text":"","category":"section"},{"location":"ProjMPO.html","page":"ProjMPO","title":"ProjMPO","text":"length(::ProjMPO)\neltype(::ProjMPO)\nsize(::ProjMPO)","category":"page"},{"location":"ProjMPO.html#Base.length-Tuple{ProjMPO}","page":"ProjMPO","title":"Base.length","text":"length(P::ProjMPO)\n\nThe length of a ProjMPO is the same as the length of the MPO used to construct it\n\n\n\n\n\n","category":"method"},{"location":"ProjMPO.html#Base.eltype-Tuple{ProjMPO}","page":"ProjMPO","title":"Base.eltype","text":"eltype(P::ProjMPO)\n\nDeduce the element type (such as Float64 or ComplexF64) of the tensors in the ProjMPO P.\n\n\n\n\n\n","category":"method"},{"location":"ProjMPO.html#Base.size-Tuple{ProjMPO}","page":"ProjMPO","title":"Base.size","text":"size(P::ProjMPO)\n\nThe size of a ProjMPO are its dimensions (d,d) when viewed as a matrix or linear operator acting on a space of dimension d.\n\nFor example, if a ProjMPO maps from a space with indices (a,s1,s2,b) to the space (a',s1',s2',b') then the size is (d,d) where d = dim(a)*dim(s1)*dim(s1)*dim(b)\n\n\n\n\n\n","category":"method"},{"location":"faq/Development.html#ITensor-Development-Frequently-Asked-Questions","page":"ITensor Development FAQs","title":"ITensor Development Frequently Asked Questions","text":"","category":"section"},{"location":"faq/Development.html#What-are-the-steps-to-contribute-code-to-ITensor?","page":"ITensor Development FAQs","title":"What are the steps to contribute code to ITensor?","text":"","category":"section"},{"location":"faq/Development.html","page":"ITensor Development FAQs","title":"ITensor Development FAQs","text":"Please contact us (support at itensor.org) if you are planning to submit a major contribution (more than a few lines of code, say). If so, we would like to discuss your plan and design before you spend significant time on it, to increase the chances we will merge your pull request.\nFork the ITensors.jl Github repo, create a new branch and make changes (commits) on that branch. ITensor imposes code formatting for contributions. Please run using JuliaFormatter; format(\".\") in the project directory to ensure formatting. As an alternative you may also use pre-commit. Install pre-commit with e.g. pip install pre-commit, then run pre-commit install in the project directory in order for pre-commit to run automatically before any commit.\nRun the ITensor unit tests by going into the test/ folder and running julia runtests.jl. To run individual test scripts, start a Julia REPL (interactive terminal) session and include each script, such as include(\"itensor.jl\").\nPush your new branch and changes to your forked repo. Github will give you the option to make a pull request (PR) out of your branch that will be submitted to us, and which you can view under the list of ITensors.jl pull requests. If your PR's tests pass and we approve your changes, we will merge it or ask you to merge it. If you merge your PR, please use the Squash and Merge option. We may also ask you to make more changes to bring your PR in line with our design goals or technical requirements.","category":"page"},{"location":"IndexSetType.html#Index-collections","page":"Index collections","title":"Index collections","text":"","category":"section"},{"location":"IndexSetType.html","page":"Index collections","title":"Index collections","text":"Collections of Index are used throughout ITensors.jl to represent the dimensions of tensors. In general, collections that are recognized and returned by ITensors.jl functions are either Vector of Index or Tuple of Index, depending on the context. For example internally an ITensor has a static number of indices so stores a Tuple of Index, while set operations like commoninds((i, j, k), (j, k, l)) will return a Vector [j, k] since the operation is inherently dynamic, i.e. the number of indices in the intersection can't in general be known before running the code. Vector of Index and Tuple of Index can usually be used interchangeably, but one or the other may be faster depending on the operation being performed.","category":"page"},{"location":"IndexSetType.html#Priming_and_tagging_IndexSet","page":"Index collections","title":"Priming and tagging","text":"","category":"section"},{"location":"IndexSetType.html","page":"Index collections","title":"Index collections","text":"Documentation for priming and tagging collections of Index can be found in the ITensor Priming and tagging section.","category":"page"},{"location":"IndexSetType.html#Set-operations","page":"Index collections","title":"Set operations","text":"","category":"section"},{"location":"IndexSetType.html","page":"Index collections","title":"Index collections","text":"Documentation for set operations involving Index collections can be found in the ITensor Index collections set operations section.","category":"page"},{"location":"IndexSetType.html#Subsets","page":"Index collections","title":"Subsets","text":"","category":"section"},{"location":"IndexSetType.html","page":"Index collections","title":"Index collections","text":"getfirst(::Function, ::IndexSet)\ngetfirst(::IndexSet)","category":"page"},{"location":"IndexSetType.html#ITensors.getfirst-Tuple{Function, Vector{IndexT} where IndexT<:Index}","page":"Index collections","title":"ITensors.getfirst","text":"getfirst(f::Function, is::Indices)\n\nGet the first Index matching the pattern function, return nothing if not found.\n\n\n\n\n\n","category":"method"},{"location":"IndexSetType.html#ITensors.getfirst-Tuple{Vector{IndexT} where IndexT<:Index}","page":"Index collections","title":"ITensors.getfirst","text":"getfirst(is::Indices)\n\nReturn the first Index in the Indices. If the Indices is empty, return nothing.\n\n\n\n\n\n","category":"method"},{"location":"IndexSetType.html#Iterating","page":"Index collections","title":"Iterating","text":"","category":"section"},{"location":"IndexSetType.html","page":"Index collections","title":"Index collections","text":"eachval(::Index...)\neachindval(::Index...)","category":"page"},{"location":"IndexSetType.html#ITensors.eachval-Tuple{Vararg{Index}}","page":"Index collections","title":"ITensors.eachval","text":"eachval(is::Index...)\neachval(is::Tuple{Vararg{Index}})\n\nCreate an iterator whose values correspond to a Cartesian indexing over the dimensions of the provided Index objects.\n\n\n\n\n\n","category":"method"},{"location":"IndexSetType.html#ITensors.eachindval-Tuple{Vararg{Index}}","page":"Index collections","title":"ITensors.eachindval","text":"eachindval(is::Index...)\neachindval(is::Tuple{Vararg{Index}})\n\nCreate an iterator whose values are Index=>value pairs corresponding to a Cartesian indexing over the dimensions of the provided Index objects.\n\nExample\n\ni = Index(3; tags=\"i\")\nj = Index(2; tags=\"j\")\nT = randomITensor(j, i)\nfor iv in eachindval(i, j)\n @show T[iv...]\nend\n\n\n\n\n\n","category":"method"},{"location":"IndexSetType.html#Symmetry-related-properties","page":"Index collections","title":"Symmetry related properties","text":"","category":"section"},{"location":"IndexSetType.html","page":"Index collections","title":"Index collections","text":"dir(::IndexSet, ::Index)","category":"page"},{"location":"IndexSetType.html#ITensors.dir-Tuple{Vector{IndexT} where IndexT<:Index, Index}","page":"Index collections","title":"ITensors.dir","text":"dir(is::Indices, i::Index)\n\nReturn the direction of the Index i in the Indices is.\n\n\n\n\n\n","category":"method"},{"location":"ContractionSequenceOptimization.html#Contraction-sequence-optimization","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"","category":"section"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"When contracting a tensor network, the sequence of contraction makes a big difference in the computational cost. However, the complexity of determining the optimal sequence grows exponentially with the number of tensors, but there are many heuristic algorithms available for computing optimal sequences for small networks[1][2][3][4][5][6]. ITensors.jl provides some functionality for helping you find the optimal contraction sequence for small tensor network, as we will show below.","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"The algorithm in ITensors.jl currently uses a modified version of[1] with simplifications for outer product contractions similar to those used in TensorOperations.jl.","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"[1]: Faster identification of optimal contraction sequences for tensor networks","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"[2]: Improving the efficiency of variational tensor network algorithms","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"[3]: Simulating quantum computation by contracting tensor networks","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"[4]: Towards a polynomial algorithm for optimal contraction sequence of tensor networks from trees","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"[5]: Algorithms for Tensor Network Contraction Ordering","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"[6]: Hyper-optimized tensor network contraction","category":"page"},{"location":"ContractionSequenceOptimization.html#Functions","page":"Contraction sequence optimization","title":"Functions","text":"","category":"section"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"ITensors.optimal_contraction_sequence\nITensors.contraction_cost\ncontract","category":"page"},{"location":"ContractionSequenceOptimization.html#ITensors.ContractionSequenceOptimization.optimal_contraction_sequence","page":"Contraction sequence optimization","title":"ITensors.ContractionSequenceOptimization.optimal_contraction_sequence","text":"optimal_contraction_sequence(T)\n\nReturns a contraction sequence for contracting the tensors T. The sequence is generally optimal (currently, outer product contractions are skipped, but some optimal sequences require outer product contractions).\n\n\n\n\n\n","category":"function"},{"location":"ContractionSequenceOptimization.html#ITensors.ContractionSequenceOptimization.contraction_cost","page":"Contraction sequence optimization","title":"ITensors.ContractionSequenceOptimization.contraction_cost","text":"contraction_cost(A; sequence)\n\nReturn the cost of contracting the collection of ITensors according to the specified sequence, where the cost is measured in the number of floating point operations that would need to be performed to contract dense tensors of the dimensions specified by the indices of the tensors (so for now, sparsity is ignored in computing the costs). Pairwise costs are returned in a vector (contracting N tensors requires N-1 pairwise contractions). You can use sum(contraction_cost(A; sequence)) to get the total cost of the contraction.\n\nIf no sequence is specified, left associative contraction is used, in other words the sequence is equivalent to [[[[1, 2], 3], 4], …].\n\n\n\n\n\n","category":"function"},{"location":"ContractionSequenceOptimization.html#NDTensors.contract","page":"Contraction sequence optimization","title":"NDTensors.contract","text":"contract(ψ::MPS, A::MPO; kwargs...) -> MPS\n*(::MPS, ::MPO; kwargs...) -> MPS\n\ncontract(A::MPO, ψ::MPS; kwargs...) -> MPS\n*(::MPO, ::MPS; kwargs...) -> MPS\n\nContract the MPO A with the MPS ψ, returning an MPS with the unique site indices of the MPO.\n\nFor example, for an MPO with site indices with prime levels of 1 and 0, such as -s'-A-s-, and an MPS with site indices with prime levels of 0, such as -s-x, the result is an MPS y with site indices with prime levels of 1, -s'-y = -s'-A-s-x.\n\nSince it is common to contract an MPO with prime levels of 1 and 0 with an MPS with prime level of 0 and want a resulting MPS with prime levels of 0, we provide a convenience function apply:\n\napply(A, x; kwargs...) = replaceprime(contract(A, x; kwargs...), 2 => 1)`.\n\nChoose the method with the method keyword, for example \"densitymatrix\" and \"naive\".\n\nKeywords\n\ncutoff::Float64=1e-13: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.\nmaxdim::Int=maxlinkdim(A) * maxlinkdim(ψ)): the maximal bond dimension of the results MPS.\nmindim::Int=1: the minimal bond dimension of the resulting MPS.\nnormalize::Bool=false: whether or not to normalize the resulting MPS.\nmethod::String=\"densitymatrix\": the algorithm to use for the contraction. Currently the options are \"densitymatrix\", where the network formed by the MPO and MPS is squared and contracted down to a density matrix which is diagonalized iteratively at each site, and \"naive\", where the MPO and MPS tensor are contracted exactly at each site and then a truncation of the resulting MPS is performed.\n\nSee also apply.\n\n\n\n\n\ncontract(A::MPO, B::MPO; kwargs...) -> MPO\n*(::MPO, ::MPO; kwargs...) -> MPO\n\nContract the MPO A with the MPO B, returning an MPO with the site indices that are not shared between A and B.\n\nIf you are contracting two MPOs with the same sets of indices, likely you want to call something like:\n\nC = contract(A', B; cutoff=1e-12)\nC = replaceprime(C, 2 => 1)\n\nThat is because if MPO A has the index structure -s'-A-s- and MPO B has the Index structure -s'-B-s-, if we only want to contract over on set of the indices, we would do (-s'-A-s-)'-s'-B-s- = -s''-A-s'-s'-B-s- = -s''-C-s-, and then map the prime levels back to pairs of primed and unprimed indices with: replaceprime(-s''-C-s-, 2 => 1) = -s'-C-s-.\n\nSince this is a common use case, you can use the convenience function:\n\nC = apply(A, B; cutoff=1e-12)\n\nwhich is the same as the code above.\n\nIf you are contracting MPOs that have diverging norms, such as MPOs representing sums of local operators, the truncation can become numerically unstable (see https://arxiv.org/abs/1909.06341 for a more numerically stable alternative). For now, you can use the following options to contract MPOs like that:\n\nC = contract(A, B; alg=\"naive\", truncate=false)\n# Bring the indices back to pairs of primed and unprimed\nC = apply(A, B; alg=\"naive\", truncate=false)\n\nKeywords\n\ncutoff::Float64=1e-14: the cutoff value for truncating the density matrix eigenvalues. Note that the default is somewhat arbitrary and subject to change, in general you should set a cutoff value.\nmaxdim::Int=maxlinkdim(A) * maxlinkdim(B)): the maximal bond dimension of the results MPS.\nmindim::Int=1: the minimal bond dimension of the resulting MPS.\nalg=\"zipup\": Either \"zipup\" or \"naive\". \"zipup\" contracts pairs of site tensors and truncates with SVDs in a sweep across the sites, while \"naive\" first contracts pairs of tensor exactly and then truncates at the end if truncate=true.\ntruncate=true: Enable or disable truncation. If truncate=false, ignore other truncation parameters like cutoff and maxdim. This is most relevant for the \"naive\" version, if you just want to contract the tensors pairwise exactly. This can be useful if you are contracting MPOs that have diverging norms, such as MPOs originating from sums of local operators.\n\nSee also apply for details about the arguments available.\n\n\n\n\n\n*(As::ITensor...; sequence = default_sequence(), kwargs...)\n*(As::Vector{<: ITensor}; sequence = default_sequence(), kwargs...)\ncontract(As::ITensor...; sequence = default_sequence(), kwargs...)\n\nContract the set of ITensors according to the contraction sequence.\n\nThe default sequence is \"automatic\" if ITensors.using_contraction_sequence_optimization() is true, otherwise it is \"left_associative\" (the ITensors are contracted from left to right).\n\nYou can change the default with ITensors.enable_contraction_sequence_optimization() and ITensors.disable_contraction_sequence_optimization().\n\nFor a custom sequence, the sequence should be provided as a binary tree where the leaves are integers n specifying the ITensor As[n] and branches are accessed by indexing with 1 or 2, i.e. sequence = Any[Any[1, 3], Any[2, 4]].\n\n\n\n\n\n","category":"function"},{"location":"ContractionSequenceOptimization.html#Examples","page":"Contraction sequence optimization","title":"Examples","text":"","category":"section"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"In the following example we show how to compute the contraction sequence cost of a","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"using ITensors\nusing Symbolics\n\nusing ITensors: contraction_cost\n\n@variables m, k, d\n\nl = Index(m, \"l\")\nr = Index(m, \"r\")\nh₁ = Index(k, \"h₁\")\nh₂ = Index(k, \"h₂\")\nh₃ = Index(k, \"h₃\")\ns₁ = Index(d, \"s₁\")\ns₂ = Index(d, \"s₂\")\n\nH₁ = emptyITensor(dag(s₁), s₁', dag(h₁), h₂)\nH₂ = emptyITensor(dag(s₂), s₂', dag(h₂), h₃)\nL = emptyITensor(dag(l), l', h₁)\nR = emptyITensor(dag(r), r', h₃)\nψ = emptyITensor(l, s₁, s₂, r)\n\nTN = [ψ, L, H₁, H₂, R]\nsequence1 = Any[2, Any[3, Any[4, Any[1, 5]]]]\nsequence2 = Any[Any[4, 5], Any[1, Any[2, 3]]]\ncost1 = contraction_cost(TN; sequence = sequence1)\ncost2 = contraction_cost(TN; sequence = sequence2)\n\nprintln(\"First sequence\")\ndisplay(sequence1)\ndisplay(cost1)\n@show sum(cost1)\n@show substitute(sum(cost1), Dict(d => 4))\n\nprintln(\"\\nSecond sequence\")\ndisplay(sequence2)\ndisplay(cost2)\n@show sum(cost2)\n@show substitute(sum(cost2), Dict(d => 4))","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"This example helps us learn that in the limit of large MPS bond dimension m, the first contraction sequence is faster, while in the limit of large MPO bond dimension k, the second sequence is faster. This has practical implications for writing an efficient DMRG algorithm in both limits, which we plan to incorporate into ITensors.jl.","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"Here is a more systematic example of searching through the parameter space to find optimal contraction sequences:","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"using ITensors\nusing Symbolics\n\nusing ITensors: contraction_cost, optimal_contraction_sequence\n\nfunction tensor_network(; m, k, d)\n l = Index(m, \"l\")\n r = Index(m, \"r\")\n h₁ = Index(k, \"h₁\")\n h₂ = Index(k, \"h₂\")\n h₃ = Index(k, \"h₃\")\n s₁ = Index(d, \"s₁\")\n s₂ = Index(d, \"s₂\")\n\n ψ = emptyITensor(l, s₁, s₂, r)\n L = emptyITensor(dag(l), l', h₁)\n H₁ = emptyITensor(dag(s₁), s₁', dag(h₁), h₂)\n H₂ = emptyITensor(dag(s₂), s₂', dag(h₂), h₃)\n R = emptyITensor(dag(r), r', h₃)\n return [ψ, L, H₁, H₂, R]\nend\n\nfunction main()\n mrange = 50:10:80\n krange = 50:10:80\n sequence_costs = Matrix{Any}(undef, length(mrange), length(krange))\n for iₘ in eachindex(mrange), iₖ in eachindex(krange)\n m_val = mrange[iₘ]\n k_val = krange[iₖ]\n d_val = 4\n\n TN = tensor_network(; m = m_val, k = k_val, d = d_val)\n sequence = optimal_contraction_sequence(TN)\n cost = contraction_cost(TN; sequence = sequence)\n\n @variables m, k, d\n TN_symbolic = tensor_network(; m = m, k = k, d = d)\n cost_symbolic = contraction_cost(TN_symbolic; sequence = sequence)\n sequence_cost = (dims = (m = m_val, k = k_val, d = d_val), sequence = sequence, cost = cost, symbolic_cost = cost_symbolic)\n sequence_costs[iₘ, iₖ] = sequence_cost\n end\n return sequence_costs\nend\n\nsequence_costs = main()\n\n# Analyze the results.\nprintln(\"Index dimensions\")\ndisplay(getindex.(sequence_costs, :dims))\n\nprintln(\"\\nContraction sequences\")\ndisplay(getindex.(sequence_costs, :sequence))\n\nprintln(\"\\nSymbolic contraction cost with d = 4\")\n# Fix d to a certain value (such as 4 for a Hubbard site)\n@variables d\nvar_sub = Dict(d => 4)\ndisplay(substitute.(sum.(getindex.(sequence_costs, :symbolic_cost)), (var_sub,)))","category":"page"},{"location":"ContractionSequenceOptimization.html","page":"Contraction sequence optimization","title":"Contraction sequence optimization","text":"A future direction will be to allow optimizing over contraction sequences with the dimensions specified symbolically, so that the optimal sequence in limits of certain dimensions can be found. In addition, we plan to implement more algorithms that work for larger networks, as well as algorithms like[2] which take an optimal sequence for a closed network and generate optimal sequences for environments of each tensor in the network, which is helpful for computing gradients of tensor networks.","category":"page"}] } diff --git a/previews/PR1410/tutorials/DMRG.html b/previews/PR1410/tutorials/DMRG.html index f0ecda2402..7ec8bca005 100644 --- a/previews/PR1410/tutorials/DMRG.html +++ b/previews/PR1410/tutorials/DMRG.html @@ -22,7 +22,7 @@ return end

        Steps of The Code

        The first two lines

        N = 100
        -sites = siteinds("S=1",N)

        tells the function siteinds to make an array of ITensor Index objects which have the properties of $S=1$ spins. This means their dimension will be 3 and they will carry the "S=1" tag, which will enable the next part of the code to know how to make appropriate operators for them.

        Try printing out some of these indices to verify their properties:

        @show sites[1]
        (dim=3|id=611|"S=1,Site,n=1")

        The next part of the code builds the Hamiltonian:

        os = OpSum()
        +sites = siteinds("S=1",N)

        tells the function siteinds to make an array of ITensor Index objects which have the properties of $S=1$ spins. This means their dimension will be 3 and they will carry the "S=1" tag, which will enable the next part of the code to know how to make appropriate operators for them.

        Try printing out some of these indices to verify their properties:

        @show sites[1]
        (dim=3|id=349|"S=1,Site,n=1")

        The next part of the code builds the Hamiltonian:

        os = OpSum()
         for j=1:N-1
           os += "Sz",j,"Sz",j+1
           os += 1/2,"S+",j,"S-",j+1
        @@ -30,4 +30,4 @@
         end
         H = MPO(os,sites)

        An OpSum is an object which accumulates Hamiltonian terms such as "Sz",1,"Sz",2 so that they can be summed afterward into a matrix product operator (MPO) tensor network. The line of code H = MPO(os,sites) constructs the Hamiltonian in the MPO format, with physical indices given by the array sites.

        The line

        psi0 = randomMPS(sites,10)

        constructs an MPS psi0 which has the physical indices sites and a bond dimension of 10. It is made by a random quantum circuit that is reshaped into an MPS, so that it will have as generic and unbiased properties as an MPS of that size can have. This choice can help prevent the DMRG calculation from getting stuck in a local minimum.

        The lines

        nsweeps = 5
         maxdim = [10,20,100,100,200]
        -cutoff = [1E-10]

        define the number of DMRG sweeps (five) we will instruct the code to do, as well as the parameters that will control the speed and accuracy of the DMRG algorithm within each sweep. The array maxdim limits the maximum MPS bond dimension allowed during each sweep and cutoff defines the truncation error goal of each sweep (if fewer values are specified than sweeps, the last value is used for all remaining sweeps).

        Finally the call

        energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff)

        runs the DMRG algorithm included in ITensor, using psi0 as an initial guess for the ground state wavefunction. The optimized MPS psi and its eigenvalue energy are returned.

        After the dmrg function returns, you can take the returned MPS psi and do further calculations with it, such as measuring local operators or computing entanglement entropy.

        +cutoff = [1E-10]

        define the number of DMRG sweeps (five) we will instruct the code to do, as well as the parameters that will control the speed and accuracy of the DMRG algorithm within each sweep. The array maxdim limits the maximum MPS bond dimension allowed during each sweep and cutoff defines the truncation error goal of each sweep (if fewer values are specified than sweeps, the last value is used for all remaining sweeps).

        Finally the call

        energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff)

        runs the DMRG algorithm included in ITensor, using psi0 as an initial guess for the ground state wavefunction. The optimized MPS psi and its eigenvalue energy are returned.

        After the dmrg function returns, you can take the returned MPS psi and do further calculations with it, such as measuring local operators or computing entanglement entropy.

        diff --git a/previews/PR1410/tutorials/MPSTimeEvolution.html b/previews/PR1410/tutorials/MPSTimeEvolution.html index fc24e4ee99..36035782bb 100644 --- a/previews/PR1410/tutorials/MPSTimeEvolution.html +++ b/previews/PR1410/tutorials/MPSTimeEvolution.html @@ -51,4 +51,4 @@ end

        Steps of The Code

        First we setsome parameters, like the system size N and time step $\tau$ to use.

        The line s = siteinds("S=1/2",N;conserve_qns=true) defines an array of spin 1/2 tensor indices (Index objects) which will be the site or physical indices of the MPS.

        Next we make an empty array gates = ITensor[] that will hold ITensors that will be our Trotter gates. Inside the for n=1:N-1 loop that follows the lines

        hj =      op("Sz",s1) * op("Sz",s2) +
             1/2 * op("S+",s1) * op("S-",s2) +
             1/2 * op("S-",s1) * op("S+",s2)

        call the op function which reads the "S=1/2" tag on our site indices (sites j and j+1) and which then knows that we want the spin 1/ 2 version of the "Sz", "S+", and "S-" operators. The op function returns these operators as ITensors and we tensor product and add them together to compute the operator $h_{j,j+1}$ defined as

        \[h_{j,j+1} = S^z_j S^z_{j+1} + \frac{1}{2} S^+_j S^-_{j+1} + \frac{1}{2} S^-_j S^+_{j+1}\]

        which we call hj in the code.

        To make the corresponding Trotter gate Gj we exponentiate hj times a factor $-i \tau/2$ and then append or push this onto the end of the gate array gates.

        Gj = exp(-im * tau/2 * hj)
        -push!(gates,Gj)

        Having made the gates for bonds (1,2),(2,3),(3,4), etc. we still need to append the gates in reverse order to complete the correct Trotter formula. Here we can conveniently do that by just calling the Julia append! function and supply a reversed version of the array of gates we have made so far. This can be done in a single line of code append!(gates,reverse(gates)).

        The line of code psi = MPS(s, n -> isodd(n) ? "Up" : "Dn") initializes our MPS psi as a product state of alternating up and down spins.

        To carry out the time evolution we loop over the range of times from 0.0 to ttotal in steps of tau, using the Julia range notation 0.0:tau:ttotal to easily set up this loop as for t in 0.0:tau:ttotal.

        Inside the loop, we use the expect function to measure the expected value of the "Sz" operator on the center site.

        To evolve the MPS to the next time, we call the function

        psi = apply(gates, psi; cutoff)

        which applies the array of ITensors called gates to our current MPS psi, truncating the MPS at each step using the truncation error threshold supplied as the variable cutoff.

        The apply function is smart enough to determine which site indices each gate has, and then figure out where to apply it to our MPS. It automatically handles truncating the MPS and can even handle non-nearest-neighbor gates, though that feature is not used in this example.

        +push!(gates,Gj)

        Having made the gates for bonds (1,2),(2,3),(3,4), etc. we still need to append the gates in reverse order to complete the correct Trotter formula. Here we can conveniently do that by just calling the Julia append! function and supply a reversed version of the array of gates we have made so far. This can be done in a single line of code append!(gates,reverse(gates)).

        The line of code psi = MPS(s, n -> isodd(n) ? "Up" : "Dn") initializes our MPS psi as a product state of alternating up and down spins.

        To carry out the time evolution we loop over the range of times from 0.0 to ttotal in steps of tau, using the Julia range notation 0.0:tau:ttotal to easily set up this loop as for t in 0.0:tau:ttotal.

        Inside the loop, we use the expect function to measure the expected value of the "Sz" operator on the center site.

        To evolve the MPS to the next time, we call the function

        psi = apply(gates, psi; cutoff)

        which applies the array of ITensors called gates to our current MPS psi, truncating the MPS at each step using the truncation error threshold supplied as the variable cutoff.

        The apply function is smart enough to determine which site indices each gate has, and then figure out where to apply it to our MPS. It automatically handles truncating the MPS and can even handle non-nearest-neighbor gates, though that feature is not used in this example.

        diff --git a/previews/PR1410/tutorials/QN_DMRG.html b/previews/PR1410/tutorials/QN_DMRG.html index 68cb750230..8f3b1eb71a 100644 --- a/previews/PR1410/tutorials/QN_DMRG.html +++ b/previews/PR1410/tutorials/QN_DMRG.html @@ -36,4 +36,4 @@ energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff) return -end +end