def jordan_wigner_code(n_modes): """ The Jordan-Wigner transform as binary code. Args: n_modes (int): number of modes Returns (BinaryCode): The Jordan-Wigner BinaryCode """ return BinaryCode(numpy.identity(n_modes, dtype=int), linearize_decoder(numpy.identity(n_modes, dtype=int)))
def bravyi_kitaev_code(n_modes): """ The Bravyi-Kitaev transform as binary code. The implementation follows arXiv:1208.5986. Args: n_modes (int): number of modes Returns (BinaryCode): The Bravyi-Kitaev BinaryCode """ return BinaryCode(_encoder_bk(n_modes), linearize_decoder(_decoder_bk(n_modes)))
def test_transform(self): code = BinaryCode([[1, 0, 0], [0, 1, 0]], ['W0', 'W1', '1 + W0 + W1']) hamiltonian = FermionOperator('0^ 2', 0.5) + FermionOperator('2^ 0', 0.5) transform = binary_code_transform(hamiltonian, code) correct_op = QubitOperator('X0 Z1',0.25) + QubitOperator('X0',0.25) self.assertTrue(transform.isclose(correct_op)) with self.assertRaises(TypeError): binary_code_transform('0^ 2', code) with self.assertRaises(TypeError): binary_code_transform(hamiltonian, ([[1, 0], [0, 1]], ['w0', 'w1']))
def weight_one_segment_code(): """ Weight-1 segment code (arXiv:1712.07067). Outputs a 3-mode, 2-qubit code, which encodes all the vectors (states) with Hamming weight (occupation) 0 and 1. n_qubits = 2, n_modes = 3. A linear amount of qubits can be saved appending several instances of this code. Note: This code is highly non-linear and might produce a lot of terms. Returns (BinaryCode): weight one segment code """ return BinaryCode([[1, 0, 1], [0, 1, 1]], ['w0 w1 + w0', 'w0 w1 + w1', ' w0 w1'])
def test_dissolve(self): code = BinaryCode([[1, 0, 0], [0, 1, 0]], ['W0', 'W1', '1 + W0 W1']) hamiltonian = FermionOperator('0^ 2', 0.5) + FermionOperator( '2^ 0', 0.5) transform = binary_code_transform(hamiltonian, code) self.assertDictEqual( transform.terms, { ((0, 'X'), (1, 'Z')): 0.375, ((0, 'X'), ): -0.125, ((0, 'Y'), ): 0.125j, ((0, 'Y'), (1, 'Z')): 0.125j }) with self.assertRaises(ValueError): dissolve(((1, '1'), ))
def test_dissolve(self): code = BinaryCode([[1, 0, 0], [0, 1, 0]], ['W0', 'W1', '1 + W0 W1']) hamiltonian = FermionOperator('0^ 2', 0.5) + FermionOperator('2^ 0', 0.5) transform = binary_code_transform(hamiltonian, code) correct_op = QubitOperator('X0 Z1', 0.375) + \ QubitOperator('X0', -0.125) + \ QubitOperator('Y0', -0.125j) + \ QubitOperator('Y0 Z1', -0.125j) self.assertTrue(transform.isclose(correct_op)) with self.assertRaises(ValueError): dissolve(((1, '1'),))
def checksum_code(n_modes, odd): """ Checksum code for either even or odd Hamming weight. The Hamming weight is defined such that it yields the total occupation number for a given basis state. A Checksum code with odd weight will encode all states with odd occupation number. This code saves one qubit: n_qubits = n_modes - 1. Args: n_modes (int): number of modes odd (int or bool): 1 (True) or 0 (False), if odd, we encode all states with odd Hamming weight Returns (BinaryCode): The checksum BinaryCode """ return BinaryCode(_encoder_checksum(n_modes), _decoder_checksum(n_modes, odd))
def test_transform(self): code = BinaryCode([[1, 0, 0], [0, 1, 0]], ['W0', 'W1', '1 + W0 + W1']) hamiltonian = FermionOperator('0^ 2', 0.5) + FermionOperator( '2^ 0', 0.5) transform = binary_code_transform(hamiltonian, code) self.assertDictEqual(transform.terms, { ((0, 'X'), (1, 'Z')): 0.25, ((0, 'X'), ): 0.25 }) with self.assertRaises(TypeError): binary_code_transform('0^ 2', code) with self.assertRaises(TypeError): binary_code_transform(hamiltonian, ([[1, 0], [0, 1]], ['w0', 'w1']))
def parity_code(n_modes): """ The parity transform (arXiv:1208.5986) as binary code. This code is very similar to the Jordan-Wigner transform, but with long update strings instead of parity strings. It does not save qubits: n_qubits = n_modes. Args: n_modes (int): number of modes Returns (BinaryCode): The parity transform BinaryCode """ dec_mtx = numpy.reshape( ([1] + [0] * (n_modes - 1)) + ([1, 1] + (n_modes - 1) * [0]) * (n_modes - 2) + [1, 1], (n_modes, n_modes)) enc_mtx = numpy.tril(numpy.ones((n_modes, n_modes), dtype=int)) return BinaryCode(enc_mtx, linearize_decoder(dec_mtx))
def weight_two_segment_code(): """ Weight-2 segment code (arXiv:1712.07067). Outputs a 5-mode, 4-qubit code, which encodes all the vectors (states) with Hamming weight (occupation) 2 and 1. n_qubits = 4, n_modes = 5. A linear amount of qubits can be saved appending several instances of this code. Note: This code is highly non-linear and might produce a lot of terms. Returns (BinaryCode): weight-2 segment code """ switch = ('w0 w1 w2 + w0 w1 w3 + w0 w2 w3 + w1 w2 w3 + w0 w1 w2 +' ' w0 w1 w2 w3') return BinaryCode( [[1, 0, 0, 0, 1], [0, 1, 0, 0, 1], [0, 0, 1, 0, 1], [0, 0, 0, 1, 1]], [ 'w0 + ' + switch, 'w1 + ' + switch, 'w2 + ' + switch, 'w3 + ' + switch, switch ])
def weight_one_binary_addressing_code(exponent): """ Weight-1 binary addressing code (arXiv:1712.07067). This highly non-linear code works for a number of modes that is an integer power of two. It encodes all possible vectors with Hamming weight 1, which corresponds to all states with total occupation one. The amount of qubits saved here is maximal: for a given argument 'exponent', we find n_modes = 2 ^ exponent, n_qubits = exponent. Note: This code is highly non-linear and might produce a lot of terms. Args: exponent (int): exponent for the number of modes n_modes = 2 ^ exponent Returns (BinaryCode): the weight one binary addressing BinaryCode """ encoder = numpy.zeros((exponent, 2**exponent), dtype=int) decoder = [0] * (2**exponent) for counter in numpy.arange(2**exponent): encoder[:, counter], decoder[counter] = \ _binary_address(exponent, counter) return BinaryCode(encoder, decoder)
def interleaved_code(modes): """ Linear code that reorders orbitals from even-odd to up-then-down. In up-then-down convention, one can append two instances of the same code 'c' in order to have two symmetric subcodes that are symmetric for spin-up and -down modes: ' c + c '. In even-odd, one can concatenate with the interleaved_code to have the same result:' interleaved_code * (c + c)'. This code changes the order of modes from (0, 1 , 2, ... , modes-1 ) to (0, modes/2, 1 modes/2+1, ... , modes-1, modes/2 - 1). n_qubits = n_modes. Args: modes (int): number of modes, must be even Returns (BinaryCode): code that interleaves orbitals """ if modes % 2 == 1: raise ValueError('number of modes must be even') else: mtx = numpy.zeros((modes, modes), dtype=int) for index in numpy.arange(modes // 2, dtype=int): mtx[index, 2 * index] = 1 mtx[modes // 2 + index, 2 * index + 1] = 1 return BinaryCode(mtx, linearize_decoder(mtx.transpose()))