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, with neighbourhoods can always be written in the form,
where , are operators acting on a single site of the lattice, , is the jth displacement defining the neighbourhood, , and 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 , while the second element of the list corresponds to , 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 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.
}