def test_wrong_term_length(self): """Test exception raised for wrong terms""" with pytest.raises(BoseHubbardError): H = bose_hubbard(2, 2, 1, 0.1, 0.2) H -= BosonOperator('5^ 5 5^') H += BosonOperator('5^ 5') extract_onsite_chemical(H)
def squeezing(r, phi=0, mode=0, hbar=2): r"""Returns the Hamiltonian of the squeezing operation. The time evolution unitary associated with squeezing is .. math:: S(r,\phi) = \exp\left(\frac{r}{2}\left(e^{-i\phi}\a^2 -e^{i\phi}{\ad}^{2} \right) \right) Therefore, :math:`U=e^{-iHt/\hbar}` where :math:`H = \frac{i\hbar}{2}\left(e^{-i\phi}\a^2 -e^{i\phi}{\ad}^{2}\right)` and :math:`t=r`. Args: r (float): the squeezing magnitude phi (float): the quadrature angle in which the squeezing occurs. :math:`\phi=0` corresponds to squeezing in the :math:`\x` quadrature, and :math:`\phi=\pi/2` corresponds to squeezing in the :math:`\p` quadrature. mode (int): the qumode on which the operation acts hbar (float): the scaling convention chosen in the definition of the quadrature operators: :math:`[\x,\p]=i\hbar` Returns: tuple (BosonOperator, t): tuple containing the Hamiltonian representing the operation and the propagation time """ H = BosonOperator('{} {}'.format(mode, mode), np.exp(-1j * phi)) H -= BosonOperator('{}^ {}^'.format(mode, mode), np.exp(1j * phi)) return (1j / 2) * H * hbar, r
def beamsplitter(theta=np.pi / 4, phi=0, mode1=0, mode2=1, hbar=2): r"""Returns the Hamiltonian of the beamsplitter operation. The time evolution unitary associated with the beamsplitter is .. math:: B(\theta,\phi) = \exp\left(\theta (e^{i \phi} \ad_0 \a_1 - e^{-i \phi}\a_0 \ad_1) \right) Therefore, :math:`U=e^{-iHt/\hbar}` where :math:`H(\phi) = {i}{\hbar}\left(e^{i \phi} \ad_0 \a_1 - e^{-i \phi}\a_0 \ad_1\right)` and :math:`t=\theta`. Args: theta (float): transmitivity angle :math:`\theta` where :math:`t=\cos(\theta)` phi (float): phase angle :math:`\phi` where :math:`r=e^{i\phi}\sin(\theta)` mode1 (int): the first qumode :math:`\a_0` on which the operation acts mode2 (int): the second qumode :math:`\a_1` on which the operation acts hbar (float): the scaling convention chosen in the definition of the quadrature operators: :math:`[\x,\p]=i\hbar` Returns: tuple (BosonOperator, t): tuple containing the Hamiltonian representing the operation and the propagation time """ H = BosonOperator('{}^ {}'.format(mode1, mode2), np.exp(1j * (np.pi - phi))) H += BosonOperator('{} {}^'.format(mode1, mode2), -np.exp(-1j * (np.pi - phi))) return 1j * H * hbar, theta
def test_two_mode(self): q = QuadOperator('p2 q0') b = get_boson_operator(q, hbar=self.hbar) expected = -1j*self.hbar/2 \ * (BosonOperator('0') + BosonOperator('0^')) \ * (BosonOperator('2') - BosonOperator('2^')) self.assertTrue(b == expected)
def test_p_squared(self): b = self.hbar * (-BosonOperator('1^ 1^') - BosonOperator('1 1') + BosonOperator('') + 2 * BosonOperator('1^ 1')) / 2 q = normal_ordered(get_quad_operator(b, hbar=self.hbar), hbar=self.hbar) expected = QuadOperator('p1 p1') self.assertTrue(q == expected)
def test_coefficients_differ(self): """Test exception raised for differing coefficients""" self.logTestName() with self.assertRaises(BoseHubbardError): H = BosonOperator('0^ 0 1^ 1', 0.5) H += BosonOperator('0^ 0 2^ 2', 1) extract_dipole(H)
def test_complex_tunneling_coefficient(self): """Test exception raised if the tunelling coefficient is complex""" self.logTestName() with self.assertRaises(BoseHubbardError): H = BosonOperator('0 1^', 1+2j) H += BosonOperator('0^ 1', 1+2j) extract_tunneling(H)
def test_ladder_wrong_form(self): """Test exception raised for wrong ladder operators""" with self.assertRaises(BoseHubbardError): H = bose_hubbard(2, 2, 1, 0) H -= BosonOperator('5^ 6^') H -= BosonOperator('6 5') extract_tunneling(H)
def two_mode_squeezing(r, phi=0, mode1=0, mode2=1, hbar=2): r"""Returns the Hamiltonian of the two-mode squeezing operation. The time evolution unitary associated with two-mode squeezing is .. math:: S_2(r,\phi) = \exp\left(r\left(e^{-i\phi}\a_0 \a_1 -e^{i\phi}{\ad_0} \ad_1 \right) \right) Therefore, :math:`U=e^{-iHt/\hbar}` where :math:`H = {i}{\hbar}\left(e^{-i\phi}\a_0 \a_1 -e^{i\phi}{\ad_0} \ad_1\right)` and :math:`t=r`. Args: r (float): the squeezing magnitude phi (float): the quadrature in which the squeezing occurs. :math:`\phi=0` corresponds to squeezing in the :math:`\x` quadrature, and :math:`\phi=\pi/2` corresponds to squeezing in the :math:`\p` quadrature. mode1 (int): the first qumode :math:`\a_0` on which the operation acts mode2 (int): the second qumode :math:`\a_1` on which the operation acts hbar (float): the scaling convention chosen in the definition of the quadrature operators: :math:`[\x,\p]=i\hbar` Returns: tuple (BosonOperator, t): tuple containing the Hamiltonian representing the operation and the propagation time """ H = BosonOperator('{} {}'.format(mode1, mode2), np.exp(-1j * (np.pi + phi))) H -= BosonOperator('{}^ {}^'.format(mode1, mode2), np.exp(1j * (np.pi + phi))) return 1j * H * hbar, r
def displacement(alpha, mode=0, hbar=2): r"""Returns the Hamiltonian of the displacement operation. The time evolution unitary associated with displacement is .. math:: D(\alpha) = \exp( \alpha \ad -\alpha^* \a) = \exp(r (e^{i\phi}\ad -e^{-i\phi}\a)) where :math:`\alpha=r e^{i \phi}` with :math:`r \geq 0` and :math:`\phi \in [0,2 \pi)`. Therefore, :math:`U=e^{-iHt/\hbar}` where :math:`H = {i}{\hbar}(e^{i\phi}\ad -e^{-i\phi}\a)` and :math:`t=r`. Args: a (complex): the displacement in the phase space mode (int): the qumode on which the operation acts hbar (float): the scaling convention chosen in the definition of the quadrature operators: :math:`[\x,\p]=i\hbar` Returns: tuple (BosonOperator, t): tuple containing the Hamiltonian representing the operation and the propagation time """ if alpha == 0.: return BosonOperator(''), 0 r = np.abs(alpha) phi = np.angle(alpha) H = BosonOperator('{}^'.format(mode), np.exp(1j * phi)) H -= BosonOperator('{}'.format(mode), np.exp(-1j * phi)) return 1j * H * hbar, r
def test_differing_onsite(self): """Test exception raised for differing coefficients""" with pytest.raises(BoseHubbardError): H = bose_hubbard(2, 2, 1, 0.1, 0.2) H -= BosonOperator('5^ 5 5^ 5') H += BosonOperator('5^ 5') extract_onsite_chemical(H)
def test_incorrect_ladders(self): """Test exception raised for wrong ladder operators""" with pytest.raises(BoseHubbardError): H = bose_hubbard(2, 2, 1, 0.1, 0.2) H -= BosonOperator('5^ 5^ 5^ 5^') H -= BosonOperator('5 5^ 5 5^') extract_onsite_chemical(H)
def get_boson_operator(operator, hbar=1.): """Convert to BosonOperator. Args: operator: QuadOperator. hbar (float): the value of hbar used in the definition of the commutator [q_i, p_j] = i hbar delta_ij. By default hbar=1. Returns: boson_operator: An instance of the BosonOperator class. """ boson_operator = BosonOperator() if isinstance(operator, QuadOperator): for term, coefficient in operator.terms.items(): tmp = BosonOperator('', coefficient) for i, d in term: if d == 'q': coeff = numpy.sqrt(hbar / 2) sign = 1 elif d == 'p': coeff = -1j * numpy.sqrt(hbar / 2) sign = -1 tmp *= coeff * (BosonOperator(((i, 0))) + BosonOperator( ((i, 1)), sign)) boson_operator += tmp else: raise TypeError("Only QuadOperator is currently " "supported for get_boson_operator.") return boson_operator
def test_ladder_wrong_form(self): """Test exception raised for wrong ladder operators""" with pytest.raises(BoseHubbardError): H = bose_hubbard(2, 2, 1, 1, 1, 0.5) H += BosonOperator('5^ 5 6^ 6^') H -= BosonOperator('6 5') extract_dipole(H)
def test_q_squared(self): b = self.hbar * (BosonOperator('0^ 0^') + BosonOperator('0 0') + BosonOperator('') + 2 * BosonOperator('0^ 0')) / 2 q = normal_ordered(get_quad_operator(b, hbar=self.hbar), hbar=self.hbar) expected = QuadOperator('q0 q0') self.assertTrue(q == expected)
def load_operator(file_name=None, data_directory=None, plain_text=False): """Load FermionOperator or QubitOperator from file. Args: file_name: The name of the saved file. data_directory: Optional data directory to change from default data directory specified in config file. plain_text: Whether the input file is plain text Returns: operator: The stored FermionOperator, BosonOperator, QuadOperator, or QubitOperator Raises: TypeError: Operator of invalid type. """ file_path = get_file_path(file_name, data_directory) if plain_text: with open(file_path, 'r') as f: data = f.read() operator_type, operator_terms = data.split(":\n") if operator_type == 'FermionOperator': operator = FermionOperator(operator_terms) elif operator_type == 'BosonOperator': operator = BosonOperator(operator_terms) elif operator_type == 'QubitOperator': operator = QubitOperator(operator_terms) elif operator_type == 'QuadOperator': operator = QuadOperator(operator_terms) else: raise TypeError('Operator of invalid type.') else: with open(file_path, 'rb') as f: data = marshal.load(f) operator_type = data[0] operator_terms = data[1] if operator_type == 'FermionOperator': operator = FermionOperator() for term in operator_terms: operator += FermionOperator(term, operator_terms[term]) elif operator_type == 'BosonOperator': operator = BosonOperator() for term in operator_terms: operator += BosonOperator(term, operator_terms[term]) elif operator_type == 'QubitOperator': operator = QubitOperator() for term in operator_terms: operator += QubitOperator(term, operator_terms[term]) elif operator_type == 'QuadOperator': operator = QuadOperator() for term in operator_terms: operator += QuadOperator(term, operator_terms[term]) else: raise TypeError('Operator of invalid type.') return operator
def test_commutator_hopping_operators(self): com = commutator(3 * FermionOperator('1^ 2'), FermionOperator('2^ 3')) com = normal_ordered(com) self.assertEqual(com, FermionOperator('1^ 3', 3)) com = commutator(3 * BosonOperator('1^ 2'), BosonOperator('2^ 3')) com = normal_ordered(com) self.assertTrue(com == BosonOperator('1^ 3', 3))
def test_boson_form(self): """Test bosonic form is correct""" H = normal_ordered(get_boson_operator(self.H, hbar=self.hbar)) expected = BosonOperator('0 1', -1) expected += BosonOperator('0 1^', -1) expected += BosonOperator('0^ 1', -1) expected += BosonOperator('0^ 1^', -1) self.assertEqual(H, expected)
def test_boson_form(self, hbar): """Test bosonic form is correct""" H = normal_ordered(get_boson_operator(self.H, hbar=hbar)) expected = BosonOperator('0 1', -1) expected += BosonOperator('0 1^', -1) expected += BosonOperator('0^ 1', -1) expected += BosonOperator('0^ 1^', -1) assert H == expected
def test_commutes_number_operators(self): com = commutator(FermionOperator('4^ 3^ 4 3'), FermionOperator('2^ 2')) com = normal_ordered(com) self.assertEqual(com, FermionOperator.zero()) com = commutator(BosonOperator('4^ 3^ 4 3'), BosonOperator('2^ 2')) com = normal_ordered(com) self.assertTrue(com == BosonOperator.zero())
def test_boson_form(self): """Test bosonic form is correct""" self.logTestName() H = normal_ordered(get_boson_operator(self.H, hbar=self.hbar)) expected = BosonOperator('0 0', -0.5) expected += BosonOperator('0^ 0', -1) expected += BosonOperator('0^ 0^', -0.5) expected += BosonOperator('', -0.5) self.assertEqual(H, expected)
def test_two_term(self): b = BosonOperator('0^ 0') + BosonOperator('0 0^') q = get_quad_operator(b, hbar=self.hbar) expected = (QuadOperator('q0') - 1j*QuadOperator('p0')) \ * (QuadOperator('q0') + 1j*QuadOperator('p0')) \ + (QuadOperator('q0') + 1j*QuadOperator('p0')) \ * (QuadOperator('q0') - 1j*QuadOperator('p0')) expected /= 2 * self.hbar self.assertTrue(q == expected)
def test_commutes_identity(self): com = commutator(FermionOperator.identity(), FermionOperator('2^ 3', 2.3)) self.assertEqual(com, FermionOperator.zero()) com = commutator(BosonOperator.identity(), BosonOperator('2^ 3', 2.3)) self.assertTrue(com == BosonOperator.zero()) com = commutator(QuadOperator.identity(), QuadOperator('q2 p3', 2.3)) self.assertTrue(com == QuadOperator.zero())
def test_too_many_terms(self): """Test exception raised for wrong number of terms""" with pytest.raises(BoseHubbardError): H = bose_hubbard(2, 2, 1, 0.1, 0.2) H -= BosonOperator('0^ 0^ 0^ 0^') extract_onsite_chemical(H) with pytest.raises(BoseHubbardError): H = bose_hubbard(2, 2, 1, 0.1, 0.2) H -= BosonOperator('5^ 5 5^ 5') extract_onsite_chemical(H)
def test_too_many_terms(self): """Test exception raised for wrong number of terms""" with pytest.raises(BoseHubbardError): H = bose_hubbard(2, 2, 1, 0) H -= BosonOperator('0^ 1^') extract_tunneling(H) with pytest.raises(BoseHubbardError): H = bose_hubbard(2, 2, 1, 0) H -= BosonOperator('0^ 1') H -= BosonOperator('1^ 0') extract_tunneling(H)
def test_commutes_no_intersection(self): com = commutator(FermionOperator('2^ 3'), FermionOperator('4^ 5^ 3')) com = normal_ordered(com) self.assertEqual(com, FermionOperator.zero()) com = commutator(BosonOperator('2^ 3'), BosonOperator('4^ 5^ 3')) com = normal_ordered(com) self.assertTrue(com == BosonOperator.zero()) com = commutator(QuadOperator('q2 p3'), QuadOperator('q4 q5 p3')) com = normal_ordered(com) self.assertTrue(com == QuadOperator.zero())
def test_number_operator_nosite(self): op = number_operator(4, parity=-1) expected = (FermionOperator(((0, 1), (0, 0))) + FermionOperator( ((1, 1), (1, 0))) + FermionOperator( ((2, 1), (2, 0))) + FermionOperator(((3, 1), (3, 0)))) self.assertEqual(op, expected) op = number_operator(4, parity=1) expected = (BosonOperator(((0, 1), (0, 0))) + BosonOperator( ((1, 1), (1, 0))) + BosonOperator( ((2, 1), (2, 0))) + BosonOperator(((3, 1), (3, 0)))) self.assertTrue(op == expected)
def test_symmetric_non_hermitian(self): op = BosonOperator('0^ 0') res = symmetric_ordering(op) expected = BosonOperator('0^ 0', 0.5) \ + BosonOperator('0 0^', 0.5) self.assertTrue(res == expected) self.assertTrue(is_hermitian(res)) op = QuadOperator('q0 p0') res = symmetric_ordering(op) expected = QuadOperator('q0 p0', 0.5) \ + QuadOperator('p0 q0', 0.5) self.assertTrue(res == expected) self.assertTrue(is_hermitian(res))
def cross_kerr(kappa, cutoff): r"""Cross-Kerr interaction e^{-i\kappa n_1 n_2}. Args: kappa (float): Kerr interaction strength. cutoff (int): the Fock basis truncation of the returned unitary. Returns: array: a [cutoff, cutoff] complex array representing the action of the cross-Kerr interaction in the truncated Fock basis. """ n0 = BosonOperator('0^ 0') n1 = BosonOperator('1^ 1') U = expm(get_sparse_operator(1j*kappa*n0*n1, trunc=cutoff).toarray()) return U
def test_two_mode(self): b = BosonOperator('0^ 2') q = get_quad_operator(b, hbar=self.hbar) expected = QuadOperator('q0') - 1j * QuadOperator('p0') expected *= (QuadOperator('q2') + 1j * QuadOperator('p2')) expected /= 2 * self.hbar self.assertTrue(q == expected)