1#   Licensed under the Apache License, Version 2.0 (the "License");
2#   you may not use this file except in compliance with the License.
3#   You may obtain a copy of the License at
4#
5#       http://www.apache.org/licenses/LICENSE-2.0
6#
7#   Unless required by applicable law or agreed to in writing, software
8#   distributed under the License is distributed on an "AS IS" BASIS,
9#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10#   See the License for the specific language governing permissions and
11#   limitations under the License.
12"""Tests for term_reordering."""
13
14import unittest
15import itertools
16import numpy
17
18from openfermion.hamiltonians import number_operator
19from openfermion.ops.operators import (FermionOperator, BosonOperator,
20                                       QuadOperator)
21from openfermion.transforms.opconversions import (jordan_wigner,
22                                                  get_fermion_operator)
23from openfermion.testing.testing_utils import random_interaction_operator
24from openfermion.utils import up_then_down
25
26from openfermion.transforms.opconversions.term_reordering import (
27    normal_ordered, chemist_ordered, reorder)
28
29
30class ChemistOrderingTest(unittest.TestCase):
31
32    def test_convert_forward_back(self):
33        n_qubits = 6
34        random_operator = get_fermion_operator(
35            random_interaction_operator(n_qubits))
36        chemist_operator = chemist_ordered(random_operator)
37        normalized_chemist = normal_ordered(chemist_operator)
38        difference = normalized_chemist - normal_ordered(random_operator)
39        self.assertAlmostEqual(0., difference.induced_norm())
40
41    def test_exception(self):
42        n_qubits = 6
43        random_operator = get_fermion_operator(
44            random_interaction_operator(n_qubits))
45        bad_term = ((2, 1), (3, 1))
46        random_operator += FermionOperator(bad_term)
47        with self.assertRaises(TypeError):
48            chemist_ordered(random_operator)
49
50    def test_form(self):
51        n_qubits = 6
52        random_operator = get_fermion_operator(
53            random_interaction_operator(n_qubits))
54        chemist_operator = chemist_ordered(random_operator)
55        for term, _ in chemist_operator.terms.items():
56            if len(term) == 2 or not len(term):
57                pass
58            else:
59                self.assertTrue(term[0][1])
60                self.assertTrue(term[2][1])
61                self.assertFalse(term[1][1])
62                self.assertFalse(term[3][1])
63                self.assertTrue(term[0][0] > term[2][0])
64                self.assertTrue(term[1][0] > term[3][0])
65
66
67class TestNormalOrdering(unittest.TestCase):
68
69    def test_boson_single_term(self):
70        op = BosonOperator('4 3 2 1') + BosonOperator('3 2')
71        self.assertTrue(op == normal_ordered(op))
72
73    def test_boson_two_term(self):
74        op_b = BosonOperator(((2, 0), (4, 0), (2, 1)), 88.)
75        normal_ordered_b = normal_ordered(op_b)
76        expected = (BosonOperator(((4, 0),), 88.) + BosonOperator(
77            ((2, 1), (4, 0), (2, 0)), 88.))
78        self.assertTrue(normal_ordered_b == expected)
79
80    def test_boson_number(self):
81        number_op2 = BosonOperator(((2, 1), (2, 0)))
82        self.assertTrue(number_op2 == normal_ordered(number_op2))
83
84    def test_boson_number_reversed(self):
85        n_term_rev2 = BosonOperator(((2, 0), (2, 1)))
86        number_op2 = number_operator(3, 2, parity=1)
87        expected = BosonOperator(()) + number_op2
88        self.assertTrue(normal_ordered(n_term_rev2) == expected)
89
90    def test_boson_offsite(self):
91        op = BosonOperator(((3, 1), (2, 0)))
92        self.assertTrue(op == normal_ordered(op))
93
94    def test_boson_offsite_reversed(self):
95        op = BosonOperator(((3, 0), (2, 1)))
96        expected = BosonOperator(((2, 1), (3, 0)))
97        self.assertTrue(expected == normal_ordered(op))
98
99    def test_boson_multi(self):
100        op = BosonOperator(((2, 0), (1, 1), (2, 1)))
101        expected = (BosonOperator(((2, 1), (1, 1), (2, 0))) + BosonOperator(
102            ((1, 1),)))
103        self.assertTrue(expected == normal_ordered(op))
104
105    def test_boson_triple(self):
106        op_132 = BosonOperator(((1, 1), (3, 0), (2, 0)))
107        op_123 = BosonOperator(((1, 1), (2, 0), (3, 0)))
108        op_321 = BosonOperator(((3, 0), (2, 0), (1, 1)))
109
110        self.assertTrue(op_132 == normal_ordered(op_123))
111        self.assertTrue(op_132 == normal_ordered(op_132))
112        self.assertTrue(op_132 == normal_ordered(op_321))
113
114    def test_fermion_single_term(self):
115        op = FermionOperator('4 3 2 1') + FermionOperator('3 2')
116        self.assertTrue(op == normal_ordered(op))
117
118    def test_fermion_two_term(self):
119        op_b = FermionOperator(((2, 0), (4, 0), (2, 1)), -88.)
120        normal_ordered_b = normal_ordered(op_b)
121        expected = (FermionOperator(((4, 0),), 88.) + FermionOperator(
122            ((2, 1), (4, 0), (2, 0)), 88.))
123        self.assertTrue(normal_ordered_b == expected)
124
125    def test_fermion_number(self):
126        number_op2 = FermionOperator(((2, 1), (2, 0)))
127        self.assertTrue(number_op2 == normal_ordered(number_op2))
128
129    def test_fermion_number_reversed(self):
130        n_term_rev2 = FermionOperator(((2, 0), (2, 1)))
131        number_op2 = number_operator(3, 2)
132        expected = FermionOperator(()) - number_op2
133        self.assertTrue(normal_ordered(n_term_rev2) == expected)
134
135    def test_fermion_offsite(self):
136        op = FermionOperator(((3, 1), (2, 0)))
137        self.assertTrue(op == normal_ordered(op))
138
139    def test_fermion_offsite_reversed(self):
140        op = FermionOperator(((3, 0), (2, 1)))
141        expected = -FermionOperator(((2, 1), (3, 0)))
142        self.assertTrue(expected == normal_ordered(op))
143
144    def test_fermion_double_create(self):
145        op = FermionOperator(((2, 0), (3, 1), (3, 1)))
146        expected = FermionOperator((), 0.0)
147        self.assertTrue(expected == normal_ordered(op))
148
149    def test_fermion_double_create_separated(self):
150        op = FermionOperator(((3, 1), (2, 0), (3, 1)))
151        expected = FermionOperator((), 0.0)
152        self.assertTrue(expected == normal_ordered(op))
153
154    def test_fermion_multi(self):
155        op = FermionOperator(((2, 0), (1, 1), (2, 1)))
156        expected = (-FermionOperator(
157            ((2, 1), (1, 1), (2, 0))) - FermionOperator(((1, 1),)))
158        self.assertTrue(expected == normal_ordered(op))
159
160    def test_fermion_triple(self):
161        op_132 = FermionOperator(((1, 1), (3, 0), (2, 0)))
162        op_123 = FermionOperator(((1, 1), (2, 0), (3, 0)))
163        op_321 = FermionOperator(((3, 0), (2, 0), (1, 1)))
164
165        self.assertTrue(op_132 == normal_ordered(-op_123))
166        self.assertTrue(op_132 == normal_ordered(op_132))
167        self.assertTrue(op_132 == normal_ordered(op_321))
168
169    def test_quad_single_term(self):
170        op = QuadOperator('p4 p3 p2 p1') + QuadOperator('p3 p2')
171        self.assertTrue(op == normal_ordered(op))
172
173        op = QuadOperator('q0 p0') - QuadOperator('p0 q0')
174        expected = QuadOperator('', 2.j)
175        self.assertTrue(expected == normal_ordered(op, hbar=2.))
176
177    def test_quad_two_term(self):
178        op_b = QuadOperator('p0 q0 p3', 88.)
179        normal_ordered_b = normal_ordered(op_b, hbar=2)
180        expected = QuadOperator('p3', -88. * 2j) + QuadOperator(
181            'q0 p0 p3', 88.0)
182        self.assertTrue(normal_ordered_b == expected)
183
184    def test_quad_offsite(self):
185        op = QuadOperator(((3, 'p'), (2, 'q')))
186        self.assertTrue(op == normal_ordered(op))
187
188    def test_quad_offsite_reversed(self):
189        op = QuadOperator(((3, 'q'), (2, 'p')))
190        expected = QuadOperator(((2, 'p'), (3, 'q')))
191        self.assertTrue(expected == normal_ordered(op))
192
193    def test_quad_triple(self):
194        op_132 = QuadOperator(((1, 'p'), (3, 'q'), (2, 'q')))
195        op_123 = QuadOperator(((1, 'p'), (2, 'q'), (3, 'q')))
196        op_321 = QuadOperator(((3, 'q'), (2, 'q'), (1, 'p')))
197
198        self.assertTrue(op_132 == normal_ordered(op_123))
199        self.assertTrue(op_132 == normal_ordered(op_132))
200        self.assertTrue(op_132 == normal_ordered(op_321))
201
202    def test_interaction_operator(self):
203        for n_orbitals, real, _ in itertools.product((1, 2, 5), (True, False),
204                                                     range(5)):
205            operator = random_interaction_operator(n_orbitals, real=real)
206            normal_ordered_operator = normal_ordered(operator)
207            expected_qubit_operator = jordan_wigner(operator)
208            actual_qubit_operator = jordan_wigner(normal_ordered_operator)
209            assert expected_qubit_operator == actual_qubit_operator
210            two_body_tensor = normal_ordered_operator.two_body_tensor
211            n_orbitals = len(two_body_tensor)
212            ones = numpy.ones((n_orbitals,) * 2)
213            triu = numpy.triu(ones, 1)
214            shape = (n_orbitals**2, 1)
215            mask = (triu.reshape(shape) * ones.reshape(shape[::-1]) +
216                    ones.reshape(shape) * triu.reshape(shape[::-1])).reshape(
217                        (n_orbitals,) * 4)
218            assert numpy.allclose(mask * two_body_tensor,
219                                  numpy.zeros((n_orbitals,) * 4))
220            for term in normal_ordered_operator:
221                order = len(term) // 2
222                left_term, right_term = term[:order], term[order:]
223                assert all(i[1] == 1 for i in left_term)
224                assert all(i[1] == 0 for i in right_term)
225                assert left_term == tuple(sorted(left_term, reverse=True))
226                assert right_term == tuple(sorted(right_term, reverse=True))
227
228    def test_exceptions(self):
229        with self.assertRaises(TypeError):
230            _ = normal_ordered(1)
231
232
233class TestReorder(unittest.TestCase):
234
235    def test_reorder(self):
236
237        def shift_by_one(x, y):
238            return (x + 1) % y
239
240        operator = FermionOperator('1^ 2^ 3 4', -3.17)
241        reordered = reorder(operator, shift_by_one)
242        self.assertEqual(reordered.terms,
243                         {((2, 1), (3, 1), (4, 0), (0, 0)): -3.17})
244        reordered = reorder(operator, shift_by_one, reverse=True)
245        self.assertEqual(reordered.terms,
246                         {((0, 1), (1, 1), (2, 0), (3, 0)): -3.17})
247
248    def test_reorder_boson(self):
249        shift_by_one = lambda x, y: (x + 1) % y
250        operator = BosonOperator('1^ 2^ 3 4', -3.17)
251        reordered = reorder(operator, shift_by_one)
252        self.assertEqual(reordered.terms,
253                         {((0, 0), (2, 1), (3, 1), (4, 0)): -3.17})
254        reordered = reorder(operator, shift_by_one, reverse=True)
255        self.assertEqual(reordered.terms,
256                         {((0, 1), (1, 1), (2, 0), (3, 0)): -3.17})
257
258    def test_reorder_quad(self):
259        shift_by_one = lambda x, y: (x + 1) % y
260        operator = QuadOperator('q1 q2 p3 p4', -3.17)
261        reordered = reorder(operator, shift_by_one)
262        self.assertEqual(reordered.terms,
263                         {((0, 'p'), (2, 'q'), (3, 'q'), (4, 'p')): -3.17})
264        reordered = reorder(operator, shift_by_one, reverse=True)
265        self.assertEqual(reordered.terms,
266                         {((0, 'q'), (1, 'q'), (2, 'p'), (3, 'p')): -3.17})
267
268    def test_up_then_down(self):
269        for LadderOp in (FermionOperator, BosonOperator):
270            operator = LadderOp('1^ 2^ 3 4', -3.17)
271            reordered = reorder(operator, up_then_down)
272            reordered = reorder(reordered, up_then_down, reverse=True)
273
274            self.assertEqual(reordered.terms, operator.terms)
275            self.assertEqual(up_then_down(6, 8), 3)
276            self.assertEqual(up_then_down(3, 8), 5)
277