Skip to content

The Ansatz class

The Lattice type

A Lattice is a graph that reprents the connectivity pattern between different physical sites. Its vertices are Lanes and wherever an edge exists between two Lanes, it means that those two sites can interact locally. A Lattice is constructed by passing a Graph and a dictionary mapping Lanes to the original graph's vertices:

julia
julia> using Graphs

julia> graph = path_graph(4)
{4, 3} undirected simple Int64 graph

julia> lattice = Lattice(graph, Dict([lane"1" => 1, lane"2" => 2, lane"3" => 3, lane"4" => lane"4"]))
ERROR: MethodError: no method matching nv(::Dict{Lane{1}, Any})
The function `nv` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  nv(!Matched::Lattice)
   @ Tenet ~/work/Tenet.jl/Tenet.jl/src/Lattice.jl:116
  nv(!Matched::TreeWidthSolver.MaskedBitGraph{INT}) where INT
   @ TreeWidthSolver ~/.julia/packages/TreeWidthSolver/7H21e/src/bitgraphs.jl:26
  nv(!Matched::Graphs.Test.GenericDiGraph)
   @ Graphs ~/.julia/packages/Graphs/1ALGD/src/Test/Test.jl:88
  ...

Because the kind of graph topologies we use are quite common, we provide some shortcuts:

julia
julia> lattice = Lattice(Val(:chain), 4)
Lattice(Lane[Lane{1}((1,)), Lane{1}((2,)), Lane{1}((3,)), Lane{1}((4,))], Graphs.SimpleGraphs.SimpleGraph{Int64}(3, [[2], [1, 3], [2, 4], [3]]))

julia> lattice = Lattice(Val(:grid), 3, 5)
ERROR: MethodError: no method matching Lattice(::Val{:grid}, ::Int64, ::Int64)
The type `Lattice` exists, but no method is defined for this combination of argument types when trying to construct it.

Closest candidates are:
  Lattice(::Any, ::Any)
   @ Tenet ~/work/Tenet.jl/Tenet.jl/src/Lattice.jl:47
  Lattice(!Matched::Val{:rectangular}, ::Any, ::Any; periodic)
   @ Tenet ~/work/Tenet.jl/Tenet.jl/src/Lattice.jl:194
  Lattice(!Matched::Val{:lieb}, ::Any, ::Any)
   @ Tenet ~/work/Tenet.jl/Tenet.jl/src/Lattice.jl:205
  ...

julia> lattice = Lattice(Val(:lieb), 2, 2)
Lattice(Lane[Lane{2}((1, 1)), Lane{2}((1, 2)), Lane{2}((1, 3)), Lane{2}((1, 4)), Lane{2}((1, 5)), Lane{2}((2, 1)), Lane{2}((2, 3)), Lane{2}((2, 5)), Lane{2}((3, 1)), Lane{2}((3, 2))    Lane{2}((3, 4)), Lane{2}((3, 5)), Lane{2}((4, 1)), Lane{2}((4, 3)), Lane{2}((4, 5)), Lane{2}((5, 1)), Lane{2}((5, 2)), Lane{2}((5, 3)), Lane{2}((5, 4)), Lane{2}((5, 5))], Graphs.SimpleGraphs.SimpleGraph{Int64}(24, [[2, 6], [1, 3], [2, 4, 7], [3, 5], [4, 8], [1, 9], [3, 11], [5, 13], [6, 10, 14], [9, 11]    [11, 13], [8, 12, 16], [9, 17], [11, 19], [13, 21], [14, 18], [17, 19], [15, 18, 20], [19, 21], [16, 20]]))

The Ansatz type

A Ansatz is a Quantum Tensor Network with a fixed graph structure, represented by a Lattice.

julia
julia> lattice = Lattice(Val(:chain), 4)
Lattice(Lane[Lane{1}((1,)), Lane{1}((2,)), Lane{1}((3,)), Lane{1}((4,))], Graphs.SimpleGraphs.SimpleGraph{Int64}(3, [[2], [1, 3], [2, 4], [3]]))

julia> tn = TensorNetwork([
           Tensor(rand(2,2), (:p1, :v12)),
           Tensor(rand(2,2,4), (:p2, :v12, :v23)),
           Tensor(rand(2,4,2), (:p3, :v23, :v34)),
           Tensor(rand(2,2), (:p4, :v34)),
       ])
TensorNetwork (#tensors=4, #inds=7)

julia> qtn = Quantum(tn, Dict([site"1" => :p1, site"2" => :p2, site"3" => :p3, site"4" => :p4]))
Quantum (inputs=0, outputs=4)

julia> ansatz = Ansatz(qtn, lattice)
Ansatz (inputs=0, outputs=4)

Just as Quantum adds information to a TensorNetwork on how to map open indices to input/output physical sites, a Ansatz teaches a Quantum on how to map Lattice Lanes to Tensors.

julia
julia> tensors(ansatz; at=lane"1")
2×2 Tensor{Float64, 2, Matrix{Float64}}:
 0.797867  0.807897
 0.112541  0.768081

Because ...

julia
julia> inds(ansatz; bond=(lane"1", lane"2"))
:v12

Time Evolution

[evolve!] is a high-level wrapper for different methods used for time-evolution.

Note

In other Tensor Network and Quantum Computing libraries, you may know evolve! by the name of apply! or apply. Given that the word "apply" has many semantic acceptions, we believe that "evolve" fits better to its purpose.

julia
julia> gate = Gate([0 1; 1 0], [site"1", site"1'"])
Gate([0 1; 1 0], Site[1, 1'])

julia> evolve!(tn, gate)
ERROR: MethodError: no method matching evolve!(::TensorNetwork, ::Gate)
The function `evolve!` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  evolve!(!Matched::Tenet.AbstractAnsatz, ::Any; threshold, maxdim, normalize, kwargs...)
   @ Tenet ~/work/Tenet.jl/Tenet.jl/src/Ansatz.jl:417
  evolve!(!Matched::Canonical, !Matched::Tenet.AbstractMPS, !Matched::Tenet.AbstractMPO; kwargs...)
   @ Tenet ~/work/Tenet.jl/Tenet.jl/src/MPS.jl:640
  evolve!(!Matched::MixedCanonical, !Matched::Tenet.AbstractMPS, !Matched::Tenet.AbstractMPO; kwargs...)
   @ Tenet ~/work/Tenet.jl/Tenet.jl/src/MPS.jl:628
  ...

When applying a multi-site gate, there are different numerical methods that can be used for approximate evolution of states. As of the time of writing, only the "Simple Update" algorithm is implemented in simple_update! but we plan to implement other methods like "Full Update" algorithm in the future.

Tip

evolve! is just a wrapper over different numerical methods for evolving states. You're free to call simple_update! directly if you want.

Warning

Currently, only 2-site gates are supported.

For example, this is how you would evolve / apply a two-site local operator using both evolve!](/api/ansatz#Tenet.evolve!-Tuple{Tenet.AbstractAnsatz,%20Any}) and [simple_update!:

julia
julia> evolve!(tn, Gate(rand(2,2,2,2), [site"1", site"2", site"1'", site"2'"]))
ERROR: MethodError: no method matching evolve!(::TensorNetwork, ::Gate)
The function `evolve!` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  evolve!(!Matched::Tenet.AbstractAnsatz, ::Any; threshold, maxdim, normalize, kwargs...)
   @ Tenet ~/work/Tenet.jl/Tenet.jl/src/Ansatz.jl:417
  evolve!(!Matched::Canonical, !Matched::Tenet.AbstractMPS, !Matched::Tenet.AbstractMPO; kwargs...)
   @ Tenet ~/work/Tenet.jl/Tenet.jl/src/MPS.jl:640
  evolve!(!Matched::MixedCanonical, !Matched::Tenet.AbstractMPS, !Matched::Tenet.AbstractMPO; kwargs...)
   @ Tenet ~/work/Tenet.jl/Tenet.jl/src/MPS.jl:628
  ...

julia> simple_update!(tn, Gate(rand(2,2,2,2), [site"1", site"2", site"1'", site"2'"]))
ERROR: MethodError: no method matching simple_update!(::TensorNetwork, ::Gate)
The function `simple_update!` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  simple_update!(!Matched::Tenet.AbstractAnsatz, ::Gate; threshold, maxdim, kwargs...)
   @ Tenet ~/work/Tenet.jl/Tenet.jl/src/Ansatz.jl:449

As you operate on a Ansatz, it will make sure that the Lattice topology is preserve through different operations. So a two-site Gate between non-connected Lanes is forbidden:

julia
julia> evolve!(tn, Gate(rand(2,2,2,2), [site"1", site"4", site"1'", site"4'"])) # this errors
ERROR: MethodError: no method matching evolve!(::TensorNetwork, ::Gate)
The function `evolve!` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  evolve!(!Matched::Tenet.AbstractAnsatz, ::Any; threshold, maxdim, normalize, kwargs...)
   @ Tenet ~/work/Tenet.jl/Tenet.jl/src/Ansatz.jl:417
  evolve!(!Matched::Canonical, !Matched::Tenet.AbstractMPS, !Matched::Tenet.AbstractMPO; kwargs...)
   @ Tenet ~/work/Tenet.jl/Tenet.jl/src/MPS.jl:640
  evolve!(!Matched::MixedCanonical, !Matched::Tenet.AbstractMPS, !Matched::Tenet.AbstractMPO; kwargs...)
   @ Tenet ~/work/Tenet.jl/Tenet.jl/src/MPS.jl:628
  ...

DocumenterMermaid.MermaidScriptBlock([...])

Made with DocumenterVitepress.jl