Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bandstructure(::Function) #17

Merged
merged 13 commits into from
Apr 12, 2020
Merged

bandstructure(::Function) #17

merged 13 commits into from
Apr 12, 2020

Conversation

pablosanjose
Copy link
Owner

@pablosanjose pablosanjose commented Apr 8, 2020

This refactors the way bandstructure works. The API has now two available methods, bandstructure(::Hamiltonian) (as before) and a new bandstructure(::Function). The core algorithm is based on the second form. It now takes a function matrixf::Function -> AbstractMatrix and a mesh over its argument space. The method bandstructure(h::Hamiltonian,...) still exists but essentially just builds a function matrixf = ϕs -> bloch!(similarmatrix(h, method), h, ϕs that then gets passed to the ::Function method.

This change allows several things. First, it allows passing things to bandstructure that are more general than mere Hamiltonians. One important example is ParametricHamiltonians, for example

const hp = parametric(h, onsite!((o; x, y) -> y*o + x))
hf(x,y) = bloch(hp(x=x, y=y), (0,0))
bandstructure(hf, npoints = (10, 10))

The number of arguments of hf above is specified by the length of the tuple npoints, which is compulsory in the bandstructure(::Function) form. (Note the need to make hp const at the global scope to make hf type stable, although it is not necessary if this code is inside a function)

Another advantage is that it allows an easy way to perform bandstructure cuts of a given Hamiltonian, for example

hf(x) = bloch(h, (x,x))
bandstructure(hf, npoints = 10)

In this way, we don't need to define 1D cuts for a 2D mesh, we just create a 1D mesh for the x argument that implements the cut.

CC: @BacAmorim, @fernandopenaranda

@BacAmorim
Copy link

@pablosanjose, If I understand correctly bandstructure(hf, npoints = (10, 10)) computes a generalized bandstructure in a generalized Brillouin Zone with a 10x10 grid. Now my question is: a general parameter does not have to be defined in a compact interval. For example I might want to study how the bands evolve while changing some on-site energy within some arbitrary interval. How would this information be passed to bandstructure(hf, npoints = (10, 10))?

@pablosanjose
Copy link
Owner Author

Good point. One just builds the mesh directly. For example

bandstructure(hf, marchingmesh(0:0.1:10, range(-π,π, length=10)))

We might want to improve the interface here, so that npoints accepts ranges in addition to (or instead of!) lengths

@pablosanjose
Copy link
Owner Author

(Opened #20 to track the issue of the ranges)

@pablosanjose
Copy link
Owner Author

Moving posts from #16 here:

@BacAmorim:

As for the generation of L-dimensional meshes, which are a cut of a P-dimensional generalized parameter space (L<P). Although this mesh is embedded in higher dimensional space, it is still a L-dimensional mesh. So cant we use the marchingmesh function to generate a L-dimensional mesh and then simply push it to the higher dimensional space? For each r point in the L-dimensional generated mesh, we can send it to the P-dimensional space by multiplying it by a generalized basis matrix B which is a PxL matrix, R=B*r. This is not very different from waht we have now, where we can have a D-dimensional sistem in a E-dimensional space, and the bravais basis is ExD matrix. I am missing something?

For example, if we want to generate a mesh of the form (x,x) in a 2D space with x=0:1, We simply have to generate a 1D mesh with x=0:1. Then for each point r, we uplift it to the 2D space by multiplying R=transpose([1 1])*r.

@pablosanjose:

All this, let's recall, is mainly motivated by the possibility of allowing bloch to span parameters as well as bloch phases. To do cuts we can either of these

  1. Declare functions (which can be cumbersome to declare if we need to also preallocate a matrix for bloch! for performance) and use bandstructure(::Function) in bandstructure(::Function) #17
  2. Allow "lifting" an L-dimensional mesh to a higher L´-dimension. This is a brilliant idea @BacAmorim, I think it makes a lot of sense. The lifting could be done through a cutfunction(::SVector{L}) --> ::SVector{L´}.

@pablosanjose
Copy link
Owner Author

pablosanjose commented Apr 9, 2020

@pablosanjose, If I understand correctly bandstructure(hf, npoints = (10, 10)) computes a generalized bandstructure in a generalized Brillouin Zone with a 10x10 grid. Now my question is: a general parameter does not have to be defined in a compact interval. For example I might want to study how the bands evolve while changing some on-site energy within some arbitrary interval. How would this information be passed to bandstructure(hf, npoints = (10, 10))?

This comment demanded a rethink of the bandstructure(::Function; kw...) method. For a general function there is no reason to assume specific ranges like in the bandstructure(::Hamiltonian) case. Instead, I think one should always be explicit about the mesh to use in this case. Hence, I've now removed the method bandstructure(::Function; kw...) and left only bandstructure(::Function, mesh; kw...). So now, to sample the "bandstructure" of a matrix function f(x,y) over a specific parameter space one should always do bandstructure(f, marchingmesh(0:0.1:1, -1:0.1:1)) for example.

@pablosanjose
Copy link
Owner Author

pablosanjose commented Apr 9, 2020

Exploration of syntax for doing a bandstructure cut:

  • The functional way:
const hp = LatticePresets.honeycomb() |> hamiltonian(hopping(1)) |> parametric(hopping!((t; p) -> t * p))
const matrix = similarmatrix(hp(p=1), LinearAlgebraPackage())
hf(x, p) = bloch!(matrix, hp(p = p), (x, x))
mesh = marchingmesh(range(-π, π, length=13), 1:0.1:2)
bandstructure(hf, mesh)
  • The lifting way (relies on parameter declaration):
hp = LatticePresets.honeycomb() |> hamiltonian(hopping(1)) |> parametric(@hopping!((t; p) -> t * p))  
     # or alternative syntax for paramter declaration
cut = marchingmesh(range(-π, π, length=13), 1:0.1:2)
mesh = lift(cut, (x, p) -> (p, x, x))
bandstructure(hp, mesh)

The second relies on definitions of bloch, bloch! and similarmatrix methods acting on ParametricHamiltonians that know their parameters.

Possibilities:

  1. Expose both options to the user
  2. Only expose the second (leaving the bandstructure(::Function) undocumented for internal use only)
  3. Have only the first (current status)
  4. Other syntax options?

This requires some critical and careful thinking... Opinions welcome!

EDIT: another possibility for lifting that keeps the cut as the internal mesh for the bandstructure (which might be preferable)

hp = LatticePresets.honeycomb() |> hamiltonian(hopping(1)) |> parametric(@hopping!((t; p) -> t * p))  
     # or alternative syntax for paramter declaration
cut = marchingmesh(range(-π, π, length=13), 1:0.1:2)
bandstructure(hp, cut, lift = (x, p) -> (p, x, x))

@BacAmorim
Copy link

I am not sure what would be preferable. I don't see any clear advantage of one over the other. Besides syntax, when making the choice, we should also have in mind which of the forms would be more readily parallelized over the mesh. But I think that both forms are as easy to distribute. So probably expose only the first one since it is the current form?

@pablosanjose
Copy link
Owner Author

I think I'll go with option 1 for the moment, as it offers the most flexibility (who knows what the user might need to do at some point).

Now that #27 and #25 are merged the road is open to specializing bloch, bloch!, similarmatrix on ParametricHamiltonians, and have bandstructure(::ParametricHamiltonian) work seamlessly on mixed Bloch/parametric spaces.

@pablosanjose
Copy link
Owner Author

I am not sure what would be preferable. I don't see any clear advantage of one over the other. Besides syntax, when making the choice, we should also have in mind which of the forms would be more readily parallelized over the mesh. But I think that both forms are as easy to distribute. So probably expose only the first one since it is the current form?

Parallelization would be equally feasible with both approaches. It is indeed an important issue I haven't yet thought much about.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants