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 bravyi_kitaev_tree.py.""" 13 14import unittest 15 16import numpy 17 18from openfermion.ops.operators import (FermionOperator, QubitOperator) 19from openfermion.transforms.opconversions import bravyi_kitaev_tree, \ 20 jordan_wigner 21from openfermion.linalg import eigenspectrum 22from openfermion.hamiltonians import number_operator 23 24 25class BravyiKitaevTransformTest(unittest.TestCase): 26 27 def test_bravyi_kitaev_tree_transform(self): 28 # Check that the QubitOperators are two-term. 29 lowering = bravyi_kitaev_tree(FermionOperator(((3, 0),))) 30 raising = bravyi_kitaev_tree(FermionOperator(((3, 1),))) 31 self.assertEqual(len(raising.terms), 2) 32 self.assertEqual(len(lowering.terms), 2) 33 34 # Test the locality invariant for N=2^d qubits 35 # (c_j majorana is always log2N+1 local on qubits) 36 n_qubits = 16 37 invariant = numpy.log2(n_qubits) + 1 38 for index in range(n_qubits): 39 operator = bravyi_kitaev_tree(FermionOperator(((index, 0),)), 40 n_qubits) 41 qubit_terms = operator.terms.items() # Get the majorana terms. 42 43 for item in qubit_terms: 44 coeff = item[1] 45 46 # Identify the c majorana terms by real 47 # coefficients and check their length. 48 if not isinstance(coeff, complex): 49 self.assertEqual(len(item[0]), invariant) 50 51 # Hardcoded coefficient test on 16 qubits 52 lowering = bravyi_kitaev_tree(FermionOperator(((9, 0),)), n_qubits) 53 raising = bravyi_kitaev_tree(FermionOperator(((9, 1),)), n_qubits) 54 55 correct_operators_c = ((7, 'Z'), (8, 'Z'), (9, 'X'), (11, 'X'), (15, 56 'X')) 57 correct_operators_d = ((7, 'Z'), (9, 'Y'), (11, 'X'), (15, 'X')) 58 59 self.assertEqual(lowering.terms[correct_operators_c], 0.5) 60 self.assertEqual(lowering.terms[correct_operators_d], 0.5j) 61 self.assertEqual(raising.terms[correct_operators_d], -0.5j) 62 self.assertEqual(raising.terms[correct_operators_c], 0.5) 63 64 def test_bk_identity(self): 65 self.assertTrue( 66 bravyi_kitaev_tree(FermionOperator(())) == QubitOperator(())) 67 68 def test_bk_n_qubits_too_small(self): 69 with self.assertRaises(ValueError): 70 bravyi_kitaev_tree(FermionOperator('2^ 3^ 5 0'), n_qubits=4) 71 72 def test_bk_jw_number_operator(self): 73 # Check if number operator has the same spectrum in both 74 # BK and JW representations 75 n = number_operator(1, 0) 76 jw_n = jordan_wigner(n) 77 bk_n = bravyi_kitaev_tree(n) 78 79 # Diagonalize and make sure the spectra are the same. 80 jw_spectrum = eigenspectrum(jw_n) 81 bk_spectrum = eigenspectrum(bk_n) 82 83 self.assertAlmostEqual( 84 0., numpy.amax(numpy.absolute(jw_spectrum - bk_spectrum))) 85 86 def test_bk_jw_number_operators(self): 87 # Check if a number operator has the same spectrum in both 88 # JW and BK representations 89 n_qubits = 2 90 n1 = number_operator(n_qubits, 0) 91 n2 = number_operator(n_qubits, 1) 92 n = n1 + n2 93 94 jw_n = jordan_wigner(n) 95 bk_n = bravyi_kitaev_tree(n) 96 97 # Diagonalize and make sure the spectra are the same. 98 jw_spectrum = eigenspectrum(jw_n) 99 bk_spectrum = eigenspectrum(bk_n) 100 101 self.assertAlmostEqual( 102 0., numpy.amax(numpy.absolute(jw_spectrum - bk_spectrum))) 103 104 def test_bk_jw_number_operator_scaled(self): 105 # Check if number operator has the same spectrum in both 106 # JW and BK representations 107 n_qubits = 1 108 n = number_operator(n_qubits, 0, coefficient=2) # eigenspectrum (0,2) 109 jw_n = jordan_wigner(n) 110 bk_n = bravyi_kitaev_tree(n) 111 112 # Diagonalize and make sure the spectra are the same. 113 jw_spectrum = eigenspectrum(jw_n) 114 bk_spectrum = eigenspectrum(bk_n) 115 116 self.assertAlmostEqual( 117 0., numpy.amax(numpy.absolute(jw_spectrum - bk_spectrum))) 118 119 def test_bk_jw_hopping_operator(self): 120 # Check if the spectrum fits for a single hoppping operator 121 ho = FermionOperator(((1, 1), (4, 0))) + FermionOperator( 122 ((4, 1), (1, 0))) 123 jw_ho = jordan_wigner(ho) 124 bk_ho = bravyi_kitaev_tree(ho) 125 126 # Diagonalize and make sure the spectra are the same. 127 jw_spectrum = eigenspectrum(jw_ho) 128 bk_spectrum = eigenspectrum(bk_ho) 129 130 self.assertAlmostEqual( 131 0., numpy.amax(numpy.absolute(jw_spectrum - bk_spectrum))) 132 133 def test_bk_jw_majoranas(self): 134 # Check if the Majorana operators have the same spectrum 135 # irrespectively of the transform. 136 137 a = FermionOperator(((1, 0),)) 138 a_dag = FermionOperator(((1, 1),)) 139 140 c = a + a_dag 141 d = 1j * (a_dag - a) 142 143 c_spins = [jordan_wigner(c), bravyi_kitaev_tree(c)] 144 d_spins = [jordan_wigner(d), bravyi_kitaev_tree(d)] 145 146 c_spectrum = [eigenspectrum(c_spins[0]), eigenspectrum(c_spins[1])] 147 d_spectrum = [eigenspectrum(d_spins[0]), eigenspectrum(d_spins[1])] 148 149 self.assertAlmostEqual( 150 0., numpy.amax(numpy.absolute(c_spectrum[0] - c_spectrum[1]))) 151 self.assertAlmostEqual( 152 0., numpy.amax(numpy.absolute(d_spectrum[0] - d_spectrum[1]))) 153 154 def test_bk_jw_integration(self): 155 # This is a legacy test, which was a minimal failing example when 156 # optimization for hermitian operators was used. 157 158 # Minimal failing example: 159 fo = FermionOperator(((3, 1),)) 160 161 jw = jordan_wigner(fo) 162 bk = bravyi_kitaev_tree(fo) 163 164 jw_spectrum = eigenspectrum(jw) 165 bk_spectrum = eigenspectrum(bk) 166 167 self.assertAlmostEqual( 168 0., numpy.amax(numpy.absolute(jw_spectrum - bk_spectrum))) 169 170 def test_bk_jw_integration_original(self): 171 # This is a legacy test, which was an example proposed by Ryan, 172 # failing when optimization for hermitian operators was used. 173 fermion_operator = FermionOperator(((3, 1), (2, 1), (1, 0), (0, 0)), 174 -4.3) 175 fermion_operator += FermionOperator(((3, 1), (1, 0)), 8.17) 176 fermion_operator += 3.2 * FermionOperator() 177 178 # Map to qubits and compare matrix versions. 179 jw_qubit_operator = jordan_wigner(fermion_operator) 180 bk_qubit_operator = bravyi_kitaev_tree(fermion_operator) 181 182 # Diagonalize and make sure the spectra are the same. 183 jw_spectrum = eigenspectrum(jw_qubit_operator) 184 bk_spectrum = eigenspectrum(bk_qubit_operator) 185 self.assertAlmostEqual(0., 186 numpy.amax( 187 numpy.absolute(jw_spectrum - bk_spectrum)), 188 places=5) 189