Manual
Import
To use TMS, you must first import it with
using TensorMixedStates
Sites and Systems
The first step in using TMS is the definition of your quantum system. In TMS, a system is composed of a finite number of sites numbered from 1 (1, 2, ..., N). These sites may be all identical or not.
There are seven different predefined types of site: Qubit
, Fermion
, Boson
, Spin
, Electron
, Tj
and Qboson
.
To use each of these sites and the corresponding predefined operators you need first to import the corresponding module. For example to use qubits, you need to write
using .Qubits
Note the "." before the name and the "s" at the end.
To define a site just call the corresponding creator for example
s = Qubit()
Some site creators need arguments: Boson
(for the maximum occupancy) and Spin
, for example
s = Boson(4)
s = Spin(3/2)
You can now define a quantum system by declaring the sites it contains:
system1 = System(10, Qubit())
gives you a system with 10 qubits. Systems may have different types of site, in this case you must feed System
with an array of sites
system2 = System([Qubit(), Boson(4), Fermion()])
gives you a three site system.
States
States may be in pure or mixed representation, these two possibilities are represented in TMS, by Pure()
or Mixed()
(note the parenthesis).
To create a state, we call the State creator
state1 = State(Pure(), system1, "Up")
returns a pure up state in a 10 qubit system. Predefined local states are designated by there name. Here "Up"
is a predefined state of site Qubit
.
All sites need not be all in the same local states, in which case we give State an array of local states
state2 = State(Mixed(), system2, ["+", "2", "Occ"])
Here we chose a mixed representation.
States may be added or multiplied by a number (they need to be based on the same system). For example
ghz = (State(Pure(), system1, "Up") + State(Pure(), system1, "Dn")) / 2
We can transform a pure representation into a mixed representation by
mixedstate = mix(purestate)
For mixed states there is a local mixed state "FullyMixed"
which correspond to a density matrix proportional to the identity matrix.
If you need a local state which is not predefined, it is possible to pass its vector (or matrix for mixed states) directly, For example, we could also define state1
by
state1 = State(Pure(), system1, [1., 0.])
Limits
TMS uses Matrix Product State to internally represent quantum states. It is important to control the parameters of this approximation, in particular the maximum bond dimension and the cutoff on singular values. To achieve this, many functions accept a Limits
object as keyword argument containing those parameters. It is build thus
lim = Limits(cutoff = 1e-10, maxdim = 50)
each (or both) of the arguments may be omitted in which case it corresponds to an absence of constraint for this parameter. In particular, Limits()
represents no constraint.
To apply the constraints on a state, one uses
newstate = truncate(oldstate; limits = lim)
Many functions accept such an argument. For example, when adding states instead of
state = (state1 + state2) / 2
One can write
state = +(state1, state2; limits = lim) / 2
Operators
In TMS, there are two kinds of operators: generic operators and indexed operators. For example,
X
represents the $\sigma_x$ Pauli operator for qubits. This is a generic operator, it is not applied to a specific site.
X(3)
represents the $\sigma_x$ Pauli operator applied to the system site number 3. This is an indexed operator.
Note that all predefined operator names start with a capital letter, so it is better to keep your own identifiers lowercase to prevent name collisions.
The operator system is very rich and flexible. For example, if you want to use this Hamiltonian
\[H = \sum_{i=1}^{n-1} \sigma_x(i) \sigma_x(i+1)\]
you will simply write
h = sum(X(i)X(i+1) for i in 1:n-1)
Many operations are defined on generic operators:
- addition, multiplication and power by a number
- tensor product:
X⊗X
is a two site operator (one can also writetensor(X, X)
) dag
represents the adjoint operator, for exampleC
is thec
operator for fermions anddag(C)
is $c^\dagger$.Dissipator
represents a Lindblad dissipator, for exampleDissipator(Sp)
is the jump operator that may flip a qubit toward up (Sp
is the $S^+$ operator)Gate
represents an operator to be applied as a gate on a mixed state. It is useful to define noisy gate operators, for example0.9 Gate(Id) + 0.1 Gate(X)
is a noisy gate operator that will apply an $\sigma_x$ gate 10 percent of the time.Proj
represents an operator that projects on the given state, for exampleProj("Up")
projects qubits on the up state.- the functions
exp
andsqrt
: for examplesqrt(Swap)
controlled
for qubits makes controlled gates:CX = controlled(X)
For example one can define the Rxy 2-site operator by
Rxy(t) = exp(-im * t * (X⊗X + Y⊗Y) / 4)
If this is not enough to define your favorite operator you can create new ones by specifying their matrix
myop = Operator("MyOp", [1 1 ; 1 -1] / √2)
For multiple site or mixed operators you must specify the type
Swap = Operator{Pure, 2}("Swap", [1 0 0 0 ; 0 0 1 0 ; 0 1 0 0 ; 0 0 0 1])
Finally from generic operators, we define indexed operators by simply applying them to the corresponding sites
Rxy(0.2)(2, 5)
myop(3)
Swap(4, 7)
In the case of Hamiltonian or Lindbladian evolution the Hamiltonian part is to be multiplied by -im:
evolver = -im * hamiltonian + dissipators
Algorithms
We can now work with states and operators.
We can apply gates with apply
newstate = apply(gates, oldstate; limits)
the gates
argument is an indexed operator representing the gates to apply
the keyword argument limits fixes the constraints to apply
We can compute ground states with dmrg
groundstate, energy = dmrg(hamiltonian, startstate; options...)
the options are limits
to set constraints and nsweeps
to fix the number of sweeps among others.
We can do time evolution with tdvp
and approx_W
newstate = tdvp(evolver, time, oldstate; options...)
newstate = approx_W(evolver, time, oldstate; options...)
the options are limits
for the constraints, nsweeps
for the number of step to do and for approx_W
, order
and w
for the parameters of the algorithm (order = 4, w = 2
are usually good)
For more details, see the reference or the inline help.
Measurements
Once we have created a state, we may want to measure it.
result = measure(state, X(1)X(3))
will give $\langle \psi | \sigma_x^1 \sigma_x^3 | \psi \rangle$
result = measure(state, X)
will give the array of the $\langle \psi | \sigma_x^i | \psi \rangle$
result = measure(state, (X, Y))
will give the matrix of the $\langle \psi | \sigma_x^i \sigma_y^j | \psi \rangle$
We can also measure other properties with Trace
, TraceError
, Trace2
, Purity
, Hermitianity
, HermitianityError
, EE
, Linkdim
and MemoryUsage
.
We can also ask for several measurements at the same time
results = measure(state, [X, X(2)Z(3), (X, Y), Trace, MemoryUsage])
For more details see the reference or the inline help.
High Level Interface
Framework
Most simulations follow the same pattern: start from some simple state, make some evolution and make measurements during or after the evolution and save the results to file. For these simple cases, TMS proposes a simpler interface.
A simple simulation follows a single state through a certain number of phases which act in a simple way on the state and make measurements during and/or after the evolution and save the results to file.
The following phases are available:
CreateState
: create a simple stateGroundState
: compute the ground state using dmrg (requires a pure state)ToMixed
: go from pure representation to mixed representationEvolve
: do Hamiltonian or Lindbladian evolutionGates
: apply some gatesPartialTrace
: trace the system over some sites (requires a mixed state)SteadyState
: compute the steady state of a Lindblad equation (requires a mixed state)
with these phases we define a SimData
object that describes the simulation and finally, we call
runTMS(simdata)
which executes the simulation.
Example
As an example, here is the complete code for such a simple simulation:
using TensorMixedStates, .Fermions
hamiltonian(n) = -sum(dag(C)(i)C(i+1)+dag(C)(i+1)C(i) for i in 1:n-1)
dissipators(n, gamma) = sum(Dissipator(sqrt(4gamma) * N)(i) for i in 1:n)
sim_data(n, gamma, step) = SimData(
name = "Fermion tight-binding chain with dephasing noise",
phases = [
CreateState(
type = Mixed(),
sytem = System(n, Fermion()),
state = [ iseven(i) ? "Occ" : "Emp" for i in 1:n ]),
Evolve(
duration = 4,
time_step = step,
algo = Tdvp(),
evolver = -im*hamiltonian(n) + dissipators(n, gamma),
limits = Limits(cutoff = 1e-30, maxdim = 100),
measures = [
"density.dat" => N,
"OSEE.dat" => EE(div(n, 2))
]
)
]
)
runTMS(sim_data(40, 1., 0.05))
Output
runTMS
creates a directory named after the SimData
object name
field and puts the output files there. In particular, it produces a log
file showing the progression of the computation, a prog.jl
file containing a copy of the script, a description
file containing the content of the SimData
description
field, a stamp
file containing version and date info, a running
empty file is present during the computation, in case of error an empty error
file is created.
Three keyword arguments may be given restart
(default true
) erases the directory before starting, clean
(default false
) erases the directory and does not run the simulation, output
(default nothing
) if set, does not create the directory nor any output files an redirect all output to the given io channel (useful values are stdout and devnull).
Measurements are specified in the measures
or final_measures
fields. They take the form of a pair
measures = destination => measurements
or a list of pairs. The possible measurements are described in the measurements section of this manual. There are three types of destinations:
filenames: writes the specified measurements to the given file as they are made. Special filenames are "stdout" (or "-"), "stderr", "" (for devnull)
"file.dat" => X
json filenames: filenames ending by ".json" are treated differently: data is accumulated during the simulation and written at the end in the JSON format.
"file.json" => [Purity, X(2)Z(3), (X, Y)]
Data object: data is accumulated during the simulation and stored in the
data
field of theSimulation
object returned byrunTMS
. This is useful for analyzing the data inside the program.Data("mydata") => [TraceError, X(1), Y]
The DataToFrame
function can used on the result to get a DataFrame
object (the user must import the DataFrames
package himself before using this function)
sim = runTMS(simdata)
df = DataToFrame(sim.data["mydata"])
For more information, see the reference or inline help for each phase, SimData
and runTMS
.