Skip to main content

Model

At a glance

When studying lattice Hamiltonians numerically, keeping track of indices and observables can be tedious and error prone. This is especially true when data is stored using linear indices for computational efficiency. The Model class helps organize calculations and serves as a unified interface to Aleph's scientific computing methods for lattice Hamiltonians.

As described here, a term in Hamiltonian on a Lattice, L\mathcal{L} with neighbourhoods NL\mathcal{N}\in\mathcal{L} can always be written in the form,

T^=rLνNJ(r)h^r(0)j=1νh^r+δj(ν)(j) \hat{T} = \sum_{r \in \mathcal{L}} \sum_{\nu \in \mathcal{N}} J(\vec{r})\hat{h}_{\vec{r}}^{(0)}\prod_{j=1}^{|\nu|} \hat{h}_{\vec{r} + \vec{\delta}_j(\nu)}^{(j)}

where h^(k)\hat{h}^{(k)}, are operators acting on a single site of the lattice, δj(ν)\delta_j(\nu), is the jth displacement defining the neighbourhood, ν\nu, and J(r)J(\vec{r}) is a position dependent coefficient strength. A model then contains the following data,

  • A Lattice geometry.
  • A list of NeighbourhoodRules used to define terms on the lattice.
  • A list of operator factories for producing the terms of Hamiltonian.
  • A list of operator factories for producing observables.

Creating a Model

To make this concrete, let's unpack how to make a trasnverse field Ising model on a square lattice. Like all types, a model is created with factory function of the same name. A model is constructed by providing a Lattice,

var my_model = model(lattice("square", [4,4]))

We can now add NeighbourhoodRules and Coefficients to the model using the corresponding factories,

my_model.add(coefficient("J", 1.0))
my_model.add(neighbourhood_rule("nn_x", [1,0]))
my_model.add(neighbourhood_rule("nn_y", [0,1]))

Each coefficient and neighbourhood rule is identified by a name. We'll see examples of adding position dependent coefficients later. Finally we can add terms to the model,

my_model.add(term("J", [Z, Z], "nn_x"))
my_model.add(term("J", [Z, Z], "nn_y"))

In this example, we represent the spin exchange interaction as a list of single site operators. The first element of the list corresponds to the reference position rr, while the second element of the list corresponds to r+δ1r + \delta_1, the first displacement in the NeighbourhoodRule.

We can add the transverse field term in analogous fashion,

my_model.add(coefficient("h", 1.0))
my_model.add(term("h", X, "onsite"))

Notice that in this case we didn't have to define the "onsite" NeighbourhoodRule. This is because all models understand how to process "onsite" by default.

Constructing an OperatorSum

Models can be used to construct explicit operator sums for the purposes of algebraic manipulation, or to feed into algorithmic frameworks that accept an operator sum,

var model_operator_sum = operator_sum(model)

Observables

Like the Hamiltonian, many observables are represeted as sums over terms. In this example we consider the ZZ magnetization,

my_model.measure("zmag", term(1.0, Z, "onsite"))

Notice that observables are also named for later reference. We could have referenced a coefficient for the term. In choosing not to, the coefficient defaults to a value of one. The observables can then be converted to a form suitable for analysis using one of the various conversion methods. For example, we can write,

var oberservable_ops = my_model.observable_to_opsum()

to get a dictionary of the observables converted to operator sums.

Parameter Sweeps

We can sweep through different parameters in the Hamiltonian by using the set_coefficient method. In the above example, we can examine a range of fields via,

var hmin = 0.0
var hmax = 2.0
var hsteps = 100
for(h : matrix([[0..hsteps]]).linspaced(hmin, hmax)) {
my_model.set_coefficient("h", h)
// Your methods here.
}

Documentation Contributors

James Lambert