def get_0000_state_operator(qubit_operator): """ Return a simplified QubitOperator U0 s.t. U0|0..00>=U1|0..00> Args: qubit_operator:U1 """ new_qubit_operator = QubitOperator() for pauli_and_coff in qubit_operator.get_operators(): for string_pauli in pauli_and_coff.terms: new_string = "" new_coff = 1 for terms in string_pauli: if terms[1] == 'X': new_string += "X" + str(terms[0]) + ' ' if terms[1] == 'Y': new_coff *= 1j new_string += "X" + str(terms[0]) + ' ' new_qubit_operator += new_coff * pauli_and_coff.terms[ string_pauli] * QubitOperator(new_string) new_qubit_operator.compress() return new_qubit_operator
def binary_code_transform(hamiltonian, code): """ Transforms a Hamiltonian written in fermionic basis into a Hamiltonian written in qubit basis, via a binary code. The role of the binary code is to relate the occupation vectors (v0 v1 v2 ... vN-1) that span the fermionic basis, to the qubit basis, spanned by binary vectors (w0, w1, w2, ..., wn-1). The binary code has to provide an analytic relation between the binary vectors (v0, v1, ..., vN-1) and (w0, w1, ..., wn-1), and possibly has the property N>n, when the Fermion basis is smaller than the fermionic Fock space. The binary_code_transform function can transform Fermion operators to qubit operators for custom- and qubit-saving mappings. Note: Logic multi-qubit operators are decomposed into Pauli-strings (e.g. CPhase(1,2) = 0.5 * (1 + Z1 + Z2 - Z1 Z2 ) ), which might increase the number of Hamiltonian terms drastically. Args: hamiltonian (FermionOperator): the fermionic Hamiltonian code (BinaryCode): the binary code to transform the Hamiltonian Returns (QubitOperator): the transformed Hamiltonian Raises: TypeError: if the hamiltonian is not a FermionOperator or code is not a BinaryCode """ if not isinstance(hamiltonian, FermionOperator): raise TypeError('hamiltonian provided must be a FermionOperator' 'received {}'.format(type(hamiltonian))) if not isinstance(code, BinaryCode): raise TypeError('code provided must be a BinaryCode' 'received {}'.format(type(code))) new_hamiltonian = QubitOperator() parity_list = make_parity_list(code) # for each term in hamiltonian for term, term_coefficient in hamiltonian.terms.items(): """ the updated parity and occupation account for sign changes due changed occupations mid-way in the term """ updated_parity = 0 # parity sign exponent parity_term = SymbolicBinary() changed_occupation_vector = [0] * code.n_modes transformed_term = QubitOperator(()) # keep track of indices appeared before fermionic_indices = numpy.array([]) # for each multiplier for op_idx, op_tuple in enumerate(reversed(term)): # get count exponent, parity exponent addition fermionic_indices = numpy.append(fermionic_indices, op_tuple[0]) count = numpy.count_nonzero( fermionic_indices[:op_idx] == op_tuple[0]) updated_parity += numpy.count_nonzero( fermionic_indices[:op_idx] < op_tuple[0]) # update term extracted = extractor(code.decoder[op_tuple[0]]) extracted *= (((-1)**count) * ((-1)**(op_tuple[1])) * 0.5) transformed_term *= QubitOperator((), 0.5) - extracted # update parity term and occupation vector changed_occupation_vector[op_tuple[0]] += 1 parity_term += parity_list[op_tuple[0]] # the parity sign and parity term transformed_term *= QubitOperator((), (-1)**updated_parity) transformed_term *= extractor(parity_term) # the update operator changed_qubit_vector = numpy.mod( code.encoder.dot(changed_occupation_vector), 2) for index, q_vec in enumerate(changed_qubit_vector): if q_vec: transformed_term *= QubitOperator('X' + str(index)) # append new term to new hamiltonian new_hamiltonian += term_coefficient * transformed_term new_hamiltonian.compress() return new_hamiltonian
def XBK_transform(op, r, p): n = count_qubits(op) op_terms = op.terms new_op = QubitOperator() #transform operator term by term for key in op_terms: coeff = op_terms[key] term = QubitOperator() #cycle through each of the r ancillarly qubit copies for j in range(r): for k in range(r): sign = 1 if (j < p) == (k < p) else -1 sub_term = QubitOperator('', 1) #cycle through each of the n original qubits spot = 0 for i in range(n): try: if key[spot][0] == i: char = key[spot][1] spot += 1 else: char = 'I' except IndexError: char = 'I' #use variable type to apply correct mapping if char == 'X': if j == k: sub_term = QubitOperator('', 0) break else: sub_term *= QubitOperator( '', 1 / 2) - QubitOperator( 'Z' + str(i + n * j) + ' Z' + str(i + n * k), 1 / 2) elif char == 'Y': if j == k: sub_term = QubitOperator('', 0) break else: sub_term *= QubitOperator( 'Z' + str(i + n * k), 1j / 2) - QubitOperator( 'Z' + str(i + n * j), 1j / 2) elif char == 'Z': if j == k: sub_term *= QubitOperator('Z' + str(i + n * j), 1) else: sub_term *= QubitOperator( 'Z' + str(i + n * j), 1 / 2) + QubitOperator( 'Z' + str(i + n * k), 1 / 2) else: if j == k: continue else: sub_term *= QubitOperator( '', 1 / 2) + QubitOperator( 'Z' + str(i + n * j) + ' Z' + str(i + n * k), 1 / 2) term += sign * sub_term new_op += coeff * term new_op.compress() return new_op