def test_integrate(self): # Basic test sx = SpinOperator.from_axes() sz = SpinOperator.from_axes(0.5, 'z') H = Hamiltonian(sz.matrix) L = Lindbladian.from_hamiltonian(H) rho0 = DensityOperator.from_vectors(0.5, [1, 0, 0], 0) t = np.linspace(0, 1, 100) tau = 2.0 avg = L.integrate_decaying(rho0, tau, sx) self.assertAlmostEqual(avg[0], 0.5*tau/(1+4*np.pi**2*tau**2)) # Same but with decay for g in [1.0, 2.0, 5.0, 10.0]: L = Lindbladian.from_hamiltonian(H, [(sx, g)]) avg = L.integrate_decaying(rho0, tau, sx) ap = -0.5*np.pi*g+((0.5*np.pi*g)**2-4*np.pi**2)**0.5 am = -0.5*np.pi*g-((0.5*np.pi*g)**2-4*np.pi**2)**0.5 A = ap*am/(am-ap) # Analytical solution for this case sol = np.real(0.5*A*tau*(1/((1-ap*tau)*ap)-1/((1-am*tau)*am))) self.assertAlmostEqual(avg[0], sol)
def get_starting_state(self): """Return the starting quantum state for the system Build the starting quantum state for the system as a coherently polarized muon + a thermal density matrix (using only the Zeeman terms) for every other spin for the current magnetic field, temperature, and muon polarization axis. Returns: DensityOperator -- The starting density matrix """ if self._rho0 is None: T = self._T muon_axis = self._mupol mu_i = self.spin_system.muon_index rhos = [] for i, s in enumerate(self.spin_system.spins): I = self.spin_system.I(i) if i == mu_i: r = DensityOperator.from_vectors(I, muon_axis, 0) else: # Get the Zeeman Hamiltonian for this field Sz = SpinOperator.from_axes(I, 'z') E = np.diag( Sz.matrix) * self.spin_system.gamma(i) * self.B * 1e6 if T > 0: Z = np.exp(-cnst.h * E / (cnst.k * T)) else: Z = np.where(E == np.amin(E), 1.0, 0.0) if np.sum(Z) > 0: Z /= np.sum(Z) else: Z = np.ones(len(E)) / len(E) r = DensityOperator(np.diag(Z)) rhos.append(r) self._rho0 = rhos[0] for r in rhos[1:]: self._rho0 = self._rho0.kron(r) return self._rho0
def test_integrate(self): ssys = SpinSystem(['e']) ssys.add_linear_term(0, [1, 0, 0]) # Precession around x H = ssys.hamiltonian rho0 = DensityOperator.from_vectors() # Start along z avg = H.integrate_decaying(rho0, 1.0, ssys.operator({0: 'z'})) self.assertTrue(np.isclose(avg[0], 0.5 / (1.0 + 4 * np.pi**2)))
def test_evolve(self): ssys = SpinSystem(['e']) ssys.add_linear_term(0, [1, 0, 0]) # Precession around x H = ssys.hamiltonian rho0 = DensityOperator.from_vectors() # Start along z t = np.linspace(0, 1, 100) evol = H.evolve(rho0, t, ssys.operator({0: 'z'})) self.assertTrue( np.all(np.isclose(evol[:, 0], 0.5 * np.cos(2 * np.pi * t))))
def test_superoperator(self): sx = SpinOperator.from_axes() rho0 = DensityOperator.from_vectors() lsx = SuperOperator.left_multiplier(sx) rsx = SuperOperator.right_multiplier(sx) csx = SuperOperator.commutator(sx) acsx = SuperOperator.anticommutator(sx) bksx = SuperOperator.bracket(sx) self.assertTrue(np.all((sx * rho0).matrix == (lsx * rho0).matrix)) self.assertTrue(np.all((rho0 * sx).matrix == (rsx * rho0).matrix)) self.assertTrue( np.all((sx * rho0 - rho0 * sx).matrix == (csx * rho0).matrix)) self.assertTrue( np.all((sx * rho0 + rho0 * sx).matrix == (acsx * rho0).matrix)) self.assertTrue( np.all((sx * rho0 * sx.dagger()).matrix == (bksx * rho0).matrix))
def test_evolve(self): # Basic test sx = SpinOperator.from_axes() sp = SpinOperator.from_axes(0.5, '+') sm = SpinOperator.from_axes(0.5, '-') sz = SpinOperator.from_axes(0.5, 'z') H = Hamiltonian(sz.matrix) L = Lindbladian.from_hamiltonian(H) rho0 = DensityOperator.from_vectors(0.5, [1, 0, 0], 0) t = np.linspace(0, 1, 100) evol = L.evolve(rho0, t, sx) self.assertTrue(np.all(np.isclose(evol[:, 0], 0.5*np.cos(2*np.pi*t)))) # Same but with decay for g in [1.0, 2.0, 5.0, 10.0]: L = Lindbladian.from_hamiltonian(H, [(sx, g)]) evol = L.evolve(rho0, t, sx) ap = -0.5*np.pi*g+((0.5*np.pi*g)**2-4*np.pi**2)**0.5 am = -0.5*np.pi*g-((0.5*np.pi*g)**2-4*np.pi**2)**0.5 A = ap*am/(am-ap) # Analytical solution for this case solx = np.real(0.5*A*(np.exp(ap*t)/ap-np.exp(am*t)/am)) self.assertTrue(np.all(np.isclose(evol[:, 0], solx))) gp = g*1.5 gm = g*0.5 L = Lindbladian.from_hamiltonian(H, [(sp, gp), (sm, gm)]) evol = L.evolve(rho0, t, [sx, sz]) solx = np.real(0.5*np.cos(2*np.pi*t)*np.exp(-2*np.pi*g*t)) solz = 0.25*(1-np.exp(-4*np.pi*g*t)) self.assertTrue(np.all(np.isclose(evol[:, 0], solx))) self.assertTrue(np.all(np.isclose(evol[:, 1], solz)))
def test_operations(self): sx = SpinOperator.from_axes(0.5, 'x') sy = SpinOperator.from_axes(0.5, 'y') sz = SpinOperator.from_axes(0.5, 'z') # Scalar operations self.assertTrue(np.all((2 * sx).matrix == [[0, 1], [1, 0]])) self.assertTrue(np.all((sx / 2).matrix == [[0, 0.25], [0.25, 0]])) # Operators (test commutation relations) self.assertTrue( np.all((sx * sy - sy * sx).matrix == (1.0j * sz).matrix)) self.assertTrue( np.all((sy * sz - sz * sy).matrix == (1.0j * sx).matrix)) self.assertTrue( np.all((sz * sx - sx * sz).matrix == (1.0j * sy).matrix)) self.assertTrue(np.all((sx + 0.5).matrix == 0.5 * np.ones((2, 2)))) self.assertTrue(np.all((sz - 0.5).matrix == np.diag([0, -1]))) # Test equality self.assertTrue(sx == SpinOperator.from_axes(0.5, 'x')) self.assertFalse( SpinOperator(np.eye(4)) == SpinOperator(np.eye(4), (2, 2))) # Test Kronecker product sxsz = sx.kron(sz) self.assertEqual(sxsz.dimension, (2, 2)) self.assertTrue( np.all(4 * sxsz.matrix == [[0, 0, 1, 0], [0, 0, 0, -1], [1, 0, 0, 0], [0, -1, 0, 0]])) # Test Hilbert-Schmidt product rho = DensityOperator.from_vectors(0.5, np.array([1, 1, 0]) / 2**0.5) sx = SpinOperator.from_axes(0.5, 'x') self.assertEqual(2 * np.real(rho.hilbert_schmidt(sx)), 0.5**0.5)
def test_density(self): rho = DensityOperator(np.eye(6) / 6.0, (2, 3)) rhosmall = rho.partial_trace([1]) self.assertEqual(rhosmall.dimension, (2, )) self.assertTrue(np.all(np.isclose(rhosmall.matrix, np.eye(2) / 2))) with self.assertRaises(ValueError): DensityOperator(np.array([[0, 1], [1, 0]])) with self.assertRaises(ValueError): DensityOperator(np.array([[1, 1], [0, 1]])) rho = DensityOperator.from_vectors(0.5, [1, 0, 0]) self.assertTrue(np.all(rho.matrix == np.ones((2, 2)) * 0.5)) rho = DensityOperator.from_vectors(0.5, [0, 1, 0], 0.5) self.assertTrue( np.all( np.isclose(rho.matrix, np.array([[0.5, -0.25j], [0.25j, 0.5]]))))
def evolve(self, rho0, times, operators=[]): """Time evolution of a state under this Lindbladian Perform an evolution of a state described by a DensityOperator under this Lindbladian and return either a sequence of DensityOperators or a sequence of expectation values for given SpinOperators. Arguments: rho0 {DensityOperator} -- Initial state times {ndarray} -- Times to compute the evolution for, in microseconds Keyword Arguments: operators {[SpinOperator]} -- List of SpinOperators to compute the expectation values of at each step. If omitted, the states' density matrices will be returned instead (default: {[]}) Returns: [DensityOperator | ndarray] -- DensityOperators or expectation values Raises: TypeError -- Invalid operators ValueError -- Invalid values of times or operators RuntimeError -- Hamiltonian is not hermitian """ if not isinstance(rho0, DensityOperator): raise TypeError('rho0 must be a valid DensityOperator') times = np.array(times) if len(times.shape) != 1: raise ValueError( 'times must be an array of values in microseconds') if isinstance(operators, SpinOperator): operators = [operators] if not all([isinstance(o, SpinOperator) for o in operators]): raise ValueError('operators must be a SpinOperator or a list' ' of SpinOperator objects') dim = rho0.dimension if self.dimension != dim * 2: raise ValueError('Incompatible rho0 dimension') if any([self.dimension != o.dimension * 2 for o in operators]): raise ValueError('Incompatible measure operator dimension') # Start by building the matrix L = self.matrix # Diagonalize it evals, revecs = np.linalg.eig(L) # Vec-ing the density matrix rho0 = rho0.matrix.reshape((-1, )) rho0 = np.linalg.solve(revecs, rho0) # And the operators operatorsT = np.array( [np.dot(o.matrix.T.reshape((-1, )), revecs) for o in operators]) rho = np.exp( 2.0 * np.pi * evals[None, :] * times[:, None]) * rho0[None, :] if len(operators) > 0: # Expectation values result = np.sum(operatorsT[None, :, :] * rho[:, None, :], axis=-1) else: # Density matrices result = [DensityOperator(np.dot(revecs, r), dim) for r in rho] return result
def evolve(self, rho0, times, operators=[]): """Time evolution of a state under this Hamiltonian Perform an evolution of a state described by a DensityOperator under this Hamiltonian and return either a sequence of DensityOperators or a sequence of expectation values for given SpinOperators. Arguments: rho0 {DensityOperator} -- Initial state times {ndarray} -- Times to compute the evolution for, in microseconds Keyword Arguments: operators {[SpinOperator]} -- List of SpinOperators to compute the expectation values of at each step. If omitted, the states' density matrices will be returned instead (default: {[]}) Returns: [DensityOperator | ndarray] -- DensityOperators or expectation values Raises: TypeError -- Invalid operators ValueError -- Invalid values of times or operators RuntimeError -- Hamiltonian is not hermitian """ if not isinstance(rho0, DensityOperator): raise TypeError('rho0 must be a valid DensityOperator') times = np.array(times) if len(times.shape) != 1: raise ValueError( 'times must be an array of values in microseconds') if isinstance(operators, SpinOperator): operators = [operators] if not all([isinstance(o, SpinOperator) for o in operators]): raise ValueError('operators must be a SpinOperator or a list' ' of SpinOperator objects') # Start by building the matrix H = self.matrix # Sanity check - should never happen if not np.all(H == H.T.conj()): raise RuntimeError('Hamiltonian is not hermitian') # Diagonalize it evals, evecs = np.linalg.eigh(H) # Turn the density matrix in the right basis dim = rho0.dimension rho0 = rho0.basis_change(evecs).matrix # Same for operators operatorsT = np.array( [o.basis_change(evecs).matrix.T for o in operators]) # Matrix of evolution operators ll = -2.0j * np.pi * (evals[:, None] - evals[None, :]) rho = np.exp(ll[None, :, :] * times[:, None, None]) * rho0[None, :, :] # Now, return values if len(operators) > 0: # Actually compute expectation values result = np.sum(rho[:, None, :, :] * operatorsT[None, :, :, :], axis=(2, 3)) else: # Just return density matrices sceve = evecs.T.conj() result = [DensityOperator(r, dim).basis_change(sceve) for r in rho] return result