Skip to content

Commit 30b628e

Browse files
aoowweennmpharriganmhuckaarettig
authored
Fix convention of PolynomialTensor basis change (#805)
In general, we rotate a matrix M to M' is to apply a rotation matrix R: M' = R @ M @ R.T The corresponding Einstein notation is M'^{p_1p_2} = R^{p_1}_{a_1} R^{p_2}_{b_1} M^{a_1a_2} --------- Co-authored-by: Matthew Harrigan <mpharrigan@google.com> Co-authored-by: Michael Hucka <mhucka@google.com> Co-authored-by: arettig <arettig@google.com>
1 parent b86d7c2 commit 30b628e

2 files changed

Lines changed: 34 additions & 5 deletions

File tree

src/openfermion/ops/representations/polynomial_tensor.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ def general_basis_change(general_tensor, rotation_matrix, key):
3232
r"""Change the basis of a general interaction tensor.
3333
3434
M'^{p_1p_2...p_n} = R^{p_1}_{a_1} R^{p_2}_{a_2} ...
35-
R^{p_n}_{a_n} M^{a_1a_2...a_n} R^{p_n}_{a_n}^T ...
36-
R^{p_2}_{a_2}^T R_{p_1}_{a_1}^T
35+
R^{p_n}_{a_n} M^{a_1a_2...a_n}
3736
3837
where R is the rotation matrix, M is the general tensor, M' is the
3938
transformed general tensor, and a_k and p_k are indices. The formula uses
@@ -68,7 +67,7 @@ def general_basis_change(general_tensor, rotation_matrix, key):
6867

6968
# Do the basis change through a single call of numpy.einsum. For example,
7069
# for the (1, 1, 0, 0) tensor, the call is:
71-
# numpy.einsum('abcd,aA,bB,cC,dD',
70+
# numpy.einsum('abcd,Aa,Bb,Cc,Dd',
7271
# general_tensor,
7372
# rotation_matrix.conj(),
7473
# rotation_matrix.conj(),
@@ -79,7 +78,7 @@ def general_basis_change(general_tensor, rotation_matrix, key):
7978
subscripts_first = ''.join(chr(ord('a') + i) for i in range(order))
8079

8180
# The 'Aa,Bb,Cc,Dd' part of the subscripts
82-
subscripts_rest = ','.join(chr(ord('a') + i) + chr(ord('A') + i) for i in range(order))
81+
subscripts_rest = ','.join(chr(ord('A') + i) + chr(ord('a') + i) for i in range(order))
8382

8483
subscripts = subscripts_first + ',' + subscripts_rest
8584

src/openfermion/ops/representations/polynomial_tensor_test.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,34 @@ def test_rotate_basis_reverse(self):
474474
polynomial_tensor.rotate_basis(rotation_matrix_reverse)
475475
self.assertEqual(polynomial_tensor, want_polynomial_tensor)
476476

477+
def test_rotate_basis_cyclic(self):
478+
n_qubits = 3
479+
# Cyclic permutation matrix: 0->1, 1->2, 2->0
480+
rotation_matrix = numpy.zeros((n_qubits, n_qubits))
481+
rotation_matrix[0, 1] = 1
482+
rotation_matrix[1, 2] = 1
483+
rotation_matrix[2, 0] = 1
484+
485+
one_body = numpy.arange(n_qubits**2, dtype=float).reshape((n_qubits, n_qubits))
486+
two_body = numpy.arange(n_qubits**4, dtype=float).reshape(
487+
(n_qubits, n_qubits, n_qubits, n_qubits)
488+
)
489+
polynomial_tensor = PolynomialTensor(
490+
{(): self.constant, (1, 0): one_body, (1, 1, 0, 0): two_body}
491+
)
492+
493+
# map from rotated index to old index rotation_map[new_ind] = old_ind
494+
# R maps 1->0, 2->1, 0->2. So new index 0 comes from old 1, etc.
495+
rotation_map = [1, 2, 0]
496+
ref_one_body = one_body[numpy.ix_(rotation_map, rotation_map)]
497+
ref_two_body = two_body[numpy.ix_(rotation_map, rotation_map, rotation_map, rotation_map)]
498+
ref_polynomial_tensor = PolynomialTensor(
499+
{(): self.constant, (1, 0): ref_one_body, (1, 1, 0, 0): ref_two_body}
500+
)
501+
502+
polynomial_tensor.rotate_basis(rotation_matrix)
503+
self.assertEqual(polynomial_tensor, ref_polynomial_tensor)
504+
477505
def test_rotate_basis_quadratic_hamiltonian_real(self):
478506
self.do_rotate_basis_quadratic_hamiltonian(True)
479507

@@ -492,7 +520,7 @@ def do_rotate_basis_quadratic_hamiltonian(self, real):
492520

493521
# Rotate a basis where the Hamiltonian is diagonal
494522
_, diagonalizing_unitary, _ = quad_ham.diagonalizing_bogoliubov_transform()
495-
quad_ham.rotate_basis(diagonalizing_unitary.T)
523+
quad_ham.rotate_basis(diagonalizing_unitary)
496524

497525
# Check that the rotated Hamiltonian is diagonal with the correct
498526
# orbital energies
@@ -515,6 +543,8 @@ def test_rotate_basis_max_order(self):
515543
self.assertEqual(tensor, want_tensor)
516544
# I originally wanted to test 25 and 26, but it turns out that
517545
# numpy.einsum complains "too many subscripts in einsum" before 26.
546+
# Currently, the sum of the number of output labels and combined labels
547+
# can't not exceed NPY_MAXDIMS(=32).
518548

519549
for order in [27, 28]:
520550
with self.assertRaises(ValueError):

0 commit comments

Comments
 (0)