Hi Ondrej,

Am 31.01.2010 um 08:43 schrieb Ondrej Certik:

...
I run you example and got a figure (attached). Nice!

So looking at the source code, here is the hamiltonian:

hamilton_operator = sum((ba.Ket(basis_states[i]) *
ba.Bra(basis_states[(i + 1) % len(basis_states)]) for i in
xrange(len(basis_states))))
#make hermitian
hamilton_operator += hamilton_operator.adjoint()


so is it:

H = sum_i  |i><i+1|

Yes exactly, except that in a second step I also add the hermitian conjugate, to make the hamilton operator hermitian.

H = sum_i  (  |i><i+1| + |i+1><i| )

I don't know of any particular system where this hamiltonian applies, it was just meant as a demonstration. A more "realistic" Hamiltonoperator would be for example a short Ising chain.
Define:

def sigma_z(site_index):
return ba.K('e', site_index)* ba.B('e', site_index) - ba.K('g', site_index)* ba.B('g', site_index)


def sigma_x(site_index):
return ba.K('e', site_index)* ba.B('g', site_index) + ba.K('g', site_index)* ba.B('e', site_index)

In the single site basis (|e>, |g>) these operators take on the form of the pauli matrices sigma_z and sigma_x. These operators and products or sums of them are hermitian, so they are good building blocks for spin chain systems.

#We can then get the Hamiltonian. In code:

J = 1.
Omega = .01

H = (J / 4) * sum(( sigma_z(site_index) * sigma_z( (site_index+1) % 4) for site_index in range(4)) + Omega * sum((sigma_x(site_index) for site_index in range(4))

This is now the hamilton operator for a spin chain with spin spin interaction strength J and coupling to a transverse magnetic field with fieldstrength Omega. It has also other interpretations, but this is to be expected, since basically any finite system consisting of 2d-subsystems can very easily be expressed in terms of
the (local) pauli operators


So then I start in an initial state already in coordinate representation (1, 0, 0, 0...0) which corresponds to the first basis state 'gggg'. To make this more explicit one could substitute the lines in the code "initial_state = ..."
with this:


initial_state_ket = ba.Ket('gggg')

# project the initial state onto the basis, by inserting the identity on the full space \sum_i |i><i| == identity == 1 # |psi> = 1* |psi> = (\sum_i |i><i| ) |psi> = \sum_i |i> (< i | psi>) = \sum_i c_i | i >, where c_i = < i | psi>

initial_state = np.array([Bra(b_i) * initial_state_ket for b_i in basis_states ]) #calculate the c_i # so if |psi> = |gggg> => c_0 = 1, c_k = 0 for k > 0 (if our labels go from 0 to dim - 1)
# so the c_i are basically the basis_coefficients at time zero t = 0



? Then you take some initial state and get it evolved?  Then you plot
the value of this observable:

Right, so the system state's time evolution is determined by the Schroedinger equation.

H | psi(t) > = i d/dt |psi(t)>

By diagonalizing the Hamiltonian we get its eigenbasis:

H |n(t)> = omega_n |n(t)>

Their time evolution is quite simple as can be seen by plugging it into the schroedinger equation:

omega_n |n(t)> = H |n(t)> = i d/dt |n(t)> ===> d/dt |n> = - i*omega_n | n(t)> === > |n(t)> = exp(- i*omega_n * t) |n(t=0)>

So all we have to do is express our state |psi(t)> in terms of the | n(t)>. Since H is hermitian, the time evolution of the system is unitary, i.e. it is enough to project |psi(t = 0)> onto the |n(t=0)>.
The coefficients given by this must then remain constant:

<n(t) | psi(t)> = <n(t = 0) | psi(t = 0)> =: a_n

because \sum_n |n(t)><n(t)| == \sum_n |n(t)><n(t)| ==  1

==> |psi(t)> = \sum_n (|n(t)><n(t)| )|psi(t)> = \sum_n |n(t)> (<n(t) | psi(t)> ) = \sum_n |n(t)> a_n = \sum_n a_n * exp(- i*omega_n * t) * | n(t=0)>

No we can ask, what is then the time dependance of our basis_coefficients c_i with respect to the basis_states defined at the beginning: c_i(t) := < i | psi(t)> = \sum_n a_n * exp(- i*omega_n * t) * < i | n(t=0)>

This last result corresponds exactly to the following expression, "coefficients" corresponds to (a_n) and "eigenvals" to "omega_n" (they are both arrays). Moreover < i | n(t=0)> is simply the i-th component of the n-th eigenvector and this corresponds to eigenvectors[i,n] (since the eigenvectors are returned as columns of a matrix by scipy.linalg.eigh) The np.dot effectively sums over n. The list comprehension calculates this for every time step. The result is a list of vectors, each vector representing all c_i at a particular time.

[np.dot(eigenvectors, coefficients*np.exp(-1j * eigenvals * t)) for t in times]



my_observable = ba.KetBra('gggg','gggg') #ground state projector


The expectation value of this operator gives (for every time step) the probability of finding the system in the state |gggg>.
This can be seen from:

<my_observable(t)> := <psi(t)| (|gggg><gggg|) |psi(t)> = | <gggg| psi(t)> |^2 = |c_0(t)|^2

I will extend this demo a little bit and add more comments and repost it later :)


? If you could say a few words what exactly it is computing, it'd be cool.

We can then make it an example in sympy, once we integrate it with the
code that we already have.

Ondrej

--
You received this message because you are subscribed to the Google Groups "sympy" group.
To post to this group, send email to sy...@googlegroups.com.
To unsubscribe from this group, send email to sympy+unsubscr...@googlegroups.com . For more options, visit this group at http://groups.google.com/group/sympy?hl=en .

<image.png>

--
You received this message because you are subscribed to the Google Groups 
"sympy" group.
To post to this group, send email to sy...@googlegroups.com.
To unsubscribe from this group, send email to 
sympy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sympy?hl=en.

Reply via email to