Package: qspin
This is a little package that will help with learning how quantum spin and entanglement work.
It is meant to complement some of the “theoretical minimum” lectures and other web resources:[Quantum state;Pauli matrices;Singlet state;Triplet state;Density matrix;Quantum entanglement;Entropy;Quantum logic gate]Book:Quantum Mechanics - The Theoretical Minimum, Leanoard Susskind and Art Friedman, Basic Books, 2014. (mostly chapters 6&7)http://theoreticalminimum.com/courses/quantum-mechanics/2012/winter/lecture-6and lecture-7Link to documentation on readthedocsInstallpipinstall--upgradeqspinOut-of-the box tests:$python>>>importqspin>>>qspin.all_tests()Examples of code useSpin statesup, down and linear combinations to form mixed states>>>fromqspinimportbra,ket,u,d,s0,sx,sy,sz>>>u|u>>>>d|d>>>>u+d|u>+|d>>>>i=1j>>>u+i*d|u>+i|d>Operators>>>sx# Pauli spin matrix[[01][10]]>>>sy[[0.+0.j-0.-1.j][0.+1.j0.+0.j]]>>>sz[[10][0-1]]>>>sz*u|u>>>>sz*d-|d>Expected value of an observablesz is the observable for z-component of spin, For the “up” state, the only
outcome us +1, so the expected value is +1.>>u.H*sz*u1.0>>sz.average(u)# another way to compute the average of an observable1.0(q.His Hermetian conjugate; it converts a ket to a bra, as in\(\Braket{u|s_z|u}\)).
The operator (sz in this case) is known in quantum mechanics as an observable,
meaning it measures something. Here it is the z-component of spin.
The eigenvalues of the observable are the possible outcomes the observation.
Underlying each state is a wave function. We store the wave function internally
as vector, with each component being the wave function value for the basis eigenstate.
The operators (observables) are stored as matrices, also defined on the same basis.
The assumed basis throughout qspin is\(\Ket{u}\)and\(\Ket{d}\)for single particles.>>>u|u>>>>u.phimatrix([[1.],[0.]])EigenvaluesWe can evaluate the eigenvalues and eigenvectors of observables. “.matrix” pulls out the matrix
representation of the operator.>>>importnumpyasnp>>>sz[[10][0-1]]>>>ev,evec=np.linalg.eig(sz.matrix)>>>evarray([1.,-1.])>>>evecmatrix([[1.,0.],[0.,1.]])>>>sx# spin x[[01][10]]>>>ev,evec=np.linalg.eig(sx.matrix)>>>evarray([1.,-1.])>>>evecmatrix([[0.70710678,-0.70710678],[0.70710678,0.70710678]])There is a handy ‘eig’ method that produces a list of eigenvalues and a
list of eigenvectors, with the eigenvectors being states:>>>ev,evec=sx.eig()>>>evarray([1.,=1.])>>>evec[0.707107|u>+0.707107|d>,-0.707107|u>+0.707107|d>]>>>sz.eig()(array([1.,-1.]),[|u>,|d>])Note that the spin-x observerable has the same eigenvalues as spin-z, +1 and -1. But the eigenvectors
are different, in our basis, since we are using the {\(\Ket{u}\),\(\Ket{d}\)} basis. They are\((\Ket{u} + \Ket{d})/\sqrt{2}\), which measures as sx = +1, and\((\Ket{u} - \Ket{d})/\sqrt{2}\), which measures as sx = -1.Conditional ProbabilitiesConditional probabilities are calculated using inner products of states with the
eigenvectors of the measurment, squared. So the probability
of measuring sx = +1 given the particle is prepared in state\(\Ket{u}\)is:>>>l=(u+d).N# "left" state. The .N normalizes the state>>>(bra(l)*ket(u))**2# expected value of up given left0.5>>>np.abs(l.H*u)**2# another way to do this. The .H means Hermetian conjugate; converts ket to bra0.5>>>l.prob(sx,l)1.0>>>l.prob(sx,u)0.5MeasurementThe quantum measurement of an observable involves ‘collapsing’ the state
to one of the eigen states of the obserable.>>>l=(u+d).N>>>sz.measure(l)(1.0,|u>)The result is random, either up or down
(with 50-50 probability in this case where the particle starts out in state ‘spin left’).
The measure function returns the value of the measurment, 1.0 in this case,
and the collapsed state,\(\Ket{u}\).String Representation of StateWe can use strings to refer to basis states.>>>u=ket('|u>')# or ket('u')  (the vert line and bracket are optional)>>>d=ket('|d>')# or ket('d')>>>u|u>>>>d|d>The string representation of basis functions defaults to ‘u’ and ‘d’. As
an alternative, the representation can be set to
‘0’ and ‘1’ or to up and down arrows (the later require your
terminal to have the ability to display unicode characters).>>>qspin.set_base_repr('01')>>>u=ket('0')>>>d=ket('1')>>>(u+d).N0.707107|0>+0.707107|1>Withqspin.set_base_repr('arrow'),u=ket([1,0])renders as\(\Ket{\uparrow}\)This provides cute printout, but is not too useful for string entry, since the up and
down arrows are unicode.Wave Function DefinitionStates can also be defined using the wave function, given
in the form of a matrix column vector. And it is good practice
to normalize states.>>>w=ket(np.matrix([1.,1.]).T).N>>>w0.707106781187|u>+0.707106781187|d>Form a projection operator from outer products of basis states.>>rho=ket('|u>')*bra('<u|')+ket('|d>')*bra('<d|')>># can also do this:>>u=ket('|u>');d=ket('|d>');>>rho=ket(u)*bra(u)+ket(d)*bra(d)>>>rho[[1.0.][0.1.]]>>>u1.0|u>>>>rho*u1.0|u>>>>rho*d1.0|d>Note that bra(ket(…)) and ket(bra(…)) convert, and takes care of the complex-conjugating.>>u.kind'ket'>>bra(u).kind'bra'Density Matrix and EntropyCreate a density matrix for an ensemble of single particles.>>fromqspinimportentropy>>P=[0.5,0.5]>>rho=P[0]*bra('|u>').density()+P[1]*bra('|d>').density()# make sure the probabilities add to one>>entropy(rho)# it's not zero because there is a mixture of states0.69314718055994529>>rho=(bra('|u>')+bra('|d>')).N.density()>>entropy(rho)# this is zero because all electrons are prepared in the "u+d" state0Make sure you normalize any states you define, using the post-operation .N.The von Neumannentropyis\(S = -\sum_i(p_i log(p_i))\)where\(p_i\)are the density matrix eigenvalues.
The entropy is essentially the randomness in a measurement of the quantum state. It
can be applied to any density matrix for either pure or mixed states. (A
pure state has zero entropy.)Multi-particle StatesMulti-particle states are in the space formed from the Kronecker product of Hilbert spaces
of the individual particles. Since multi-particle quantum states can be mixed states, there
are far more possible state vectors (\(2^n\)dimensional vector space) than for classical
systems (which are in only\(n\)dimensional space)We build up multi-particle states with Kronecker products ‘**’ (meaning\(\otimes\)), or with strings>>>uu=u**u>>>dd=ket('|dd>')# or ket('dd')>>>s=(d**u**u+u**d**u+d**d**u).N>>>s0.57735|udu>+0.57735|duu>+0.57735|ddu>Multi-particle operators are similarly built up with Kronecker products>>>s2x=sx**sx>>>s2x[[0001][0010][0100][1000]Partial TraceThe density matrix for a multi-particle state is\(2^n \times 2^n\). A partial
trace is a way to form the density matrix for a subset of the particles. ‘Tracing out’\(m\)of the particles results in a\(2^{n-m} \times 2^{n-m}\)density matrix.
Partial traces are important in many aspects of analyzing the multi-particle state,
including evaluating the entanglement.>>>sing=(u**d-d**u).N>>>rho=sing.density()>>>rhomatrix([[0.,0.,0.,0.],[0.,0.5,-0.5,0.],[0.,-0.5,0.5,0.],[0.,0.,0.,0.]])>>>rhoA=ptrace(rho,[1])# trace over particle 1 ('Bob') to get particle 0 ('Alice') density>>>rhoAmatrix([[0.5,0.],[0.,0.5]])Entangled StatesOnce you have created a (possibly) entangled state of two particles, you can test it for entanglement:>>>sing=(u**d-d**u).N>>>sing.entangled()True>>>(u**u).entangled()FalseThe test for entanglement is to check the entropy of one of the particles after
the other particle has been ‘traced out.’Quantum ComputingSeveral quantum logic gates are now defined in qspin including:
Hadamard, NOT, SWAP, controlled gates, square root gates, and phase shift gates.>>>fromqspinimportu,d,gate>>>SWAP=gate('SWAP')>>>SWAP*(u**d)|du>>>>H=gate('Hadamard')>>>H*u0.707|u>+0.707|d>>>>H*d0.707|u>-0.707|d>shows that SWAP interchanges the q-bits, and Hadamard makes the Bell states
from spin up and spin down.