def test_get_merged_different_hamiltonian(): hamiltonian = QubitOperator("Z2", 2) gate = te.TimeEvolution(2, hamiltonian) hamiltonian2 = QubitOperator("Y2", 2) gate2 = te.TimeEvolution(2, hamiltonian2) with pytest.raises(NotMergeable): gate.get_merged(gate2)
def _get_Hamiltonian(self, filename): 'Parse a projectq Hamiltonian operator from file into projectq operator' # get raw data with open(filename, "r", encoding='utf-8-sig') as file: lines = file.readlines() # convert to projectq operator H = QubitOperator() for line in lines: line = line.split() term = '' for i in range(len(line)): if i in [0, 1]: pass elif line[i] == str(0): pass elif line[i] == str(1): term += 'X' + str(i - 2) + ' ' elif line[i] == str(2): term += 'Y' + str(i - 2) + ' ' elif line[i] == str(3): term += 'Z' + str(i - 2) + ' ' else: raise Exception("Error: Incorrect hamiltonian file format") if self.verbose: print(term) H += float(line[1]) * QubitOperator(term) return H
def load_operator(file_name=None, data_directory=None): """Load FermionOperator or QubitOperator from file. Args: file_name: The name of the saved file. data_directory: Optional data directory to change from default data directory specified in config file. Returns: operator: The stored FermionOperator or QubitOperator Raises: TypeError: Operator of invalid type. """ file_path = get_file_path(file_name, data_directory) with open(file_path, 'rb') as f: data = marshal.load(f) operator_type = data[0] operator_terms = data[1] if operator_type == 'FermionOperator': operator = FermionOperator() for term in operator_terms: operator += FermionOperator(term, operator_terms[term]) elif operator_type == 'QubitOperator': operator = QubitOperator() for term in operator_terms: operator += QubitOperator(term, operator_terms[term]) else: raise TypeError('Operator of invalid type.') return operator
def maxcut_cost_ham(graph): ham = QubitOperator('', 0.0) for i, j in graph.edges(): operator_i = 'Z' + str(i) operator_j = 'Z' + str(j) ham += QubitOperator(operator_i + ' ' + operator_j, 0.5) + QubitOperator('', -0.5) return ham
def test_get_merged_not_close_enough(): hamiltonian = QubitOperator("Z2", 2) hamiltonian += QubitOperator("X3", 1) gate = te.TimeEvolution(2, hamiltonian) hamiltonian2 = QubitOperator("Z2", 4) hamiltonian2 += QubitOperator("X3", 2 + 1e-8) gate2 = te.TimeEvolution(5, hamiltonian2) with pytest.raises(NotMergeable): gate.get_merged(gate2)
def test_is_identity(self): self.assertTrue(is_identity(FermionOperator(()))) self.assertTrue(is_identity(2. * FermionOperator(()))) self.assertTrue(is_identity(QubitOperator(()))) self.assertTrue(is_identity(QubitOperator((), 2.))) self.assertFalse(is_identity(FermionOperator('1^'))) self.assertFalse(is_identity(QubitOperator('X1'))) self.assertFalse(is_identity(FermionOperator())) self.assertFalse(is_identity(QubitOperator()))
def jordan_wigner_dual_basis_hamiltonian(grid, geometry=None, spinless=False, include_constant=False): """Return the dual basis Hamiltonian as QubitOperator. Args: grid (Grid): The discretization to use. geometry: A list of tuples giving the coordinates of each atom. example is [('H', (0, 0, 0)), ('H', (0, 0, 0.7414))]. Distances in atomic units. Use atomic symbols to specify atoms. spinless (bool): Whether to use the spinless model or not. include_constant (bool): Whether to include the Madelung constant. Returns: hamiltonian (QubitOperator) """ jellium_op = jordan_wigner_dual_basis_jellium( grid, spinless, include_constant) if geometry is None: return jellium_op for item in geometry: if len(item[1]) != grid.dimensions: raise ValueError("Invalid geometry coordinate.") if item[0] not in periodic_hash_table: raise ValueError("Invalid nuclear element.") n_orbitals = grid.num_points() volume = grid.volume_scale() if spinless: n_qubits = n_orbitals else: n_qubits = 2 * n_orbitals prefactor = -2 * numpy.pi / volume external_potential = QubitOperator() for k_indices in grid.all_points_indices(): momenta = momentum_vector(k_indices, grid) momenta_squared = momenta.dot(momenta) if momenta_squared < EQ_TOLERANCE: continue for p in range(n_qubits): index_p = grid_indices(p, grid, spinless) coordinate_p = position_vector(index_p, grid) for nuclear_term in geometry: coordinate_j = numpy.array(nuclear_term[1], float) exp_index = 1.0j * momenta.dot(coordinate_j - coordinate_p) coefficient = (prefactor / momenta_squared * periodic_hash_table[nuclear_term[0]] * numpy.exp(exp_index)) external_potential += (QubitOperator((), coefficient) - QubitOperator(((p, 'Z'),), coefficient)) return jellium_op + external_potential
def test_get_merged_one_term(): hamiltonian = QubitOperator("Z2", 2) gate = te.TimeEvolution(2, hamiltonian) hamiltonian2 = QubitOperator("Z2", 4) gate2 = te.TimeEvolution(5, hamiltonian2) merged = gate.get_merged(gate2) # This is not a requirement, the hamiltonian could also be the other # if we change implementation assert merged.hamiltonian.isclose(hamiltonian) assert merged.time == pytest.approx(12)
def test_sparse_matrix_combo(self): qop = (QubitOperator(((0, 'Y'), (1, 'X')), -0.1j) + QubitOperator( ((0, 'X'), (1, 'Z')), 3. + 2.j)) sparse_operator = get_sparse_operator(qop) self.assertEqual( list(sparse_operator.data), [3 + 2j, 0.1, 0.1, -3 - 2j, 3 + 2j, -0.1, -0.1, -3 - 2j]) self.assertEqual(list(sparse_operator.indices), [2, 3, 2, 3, 0, 1, 0, 1])
def test_z(self): pauli_z = QubitOperator(((2, 'Z'), )) transmed_z = reverse_jordan_wigner(pauli_z) expected = (FermionOperator(()) + FermionOperator( ((2, 1), (2, 0)), -2.)) self.assertTrue(transmed_z.isclose(expected)) retransmed_z = jordan_wigner(transmed_z) self.assertTrue(pauli_z.isclose(retransmed_z))
def test_error_operator_xyz(self): terms = [QubitOperator('X1'), QubitOperator('Y1'), QubitOperator('Z1')] expected = numpy.array([[-2./3, 1./3 + 1.j/6, 0., 0.], [1./3 - 1.j/6, 2./3, 0., 0.], [0., 0., -2./3, 1./3 + 1.j/6], [0., 0., 1./3 - 1.j/6, 2./3]]) sparse_op = get_sparse_operator(error_operator(terms)) matrix = sparse_op.todense() self.assertTrue(numpy.allclose(matrix, expected), ("Got " + str(matrix)))
def single_hamiltonian(clause: list): # separate variables number and its negation signs var0, var1, var2 = np.abs(clause) - 1 # explanation for -1: # qubit numeration starts with 0, but clause literals # starts with 1 in order to not loose negation of 0 literal # so here 1 is subtracted to not keep 0-s qubit unused sign0, sign1, sign2 = [int(i) for i in np.sign(clause)] # create expanded Hamiltonian from the form 0.125*(1+z_i)(1+z_j)(1+z_k) ham0 = QubitOperator(' ') ham1 = QubitOperator(f'Z{var0}', sign0) ham2 = QubitOperator(f'Z{var1}', sign1) ham3 = QubitOperator(f'Z{var2}', sign2) ham4 = QubitOperator(f'Z{var0} Z{var1}', sign0 * sign1) ham5 = QubitOperator(f'Z{var0} Z{var2}', sign0 * sign2) ham6 = QubitOperator(f'Z{var1} Z{var2}', sign1 * sign2) ham7 = QubitOperator(f'Z{var0} Z{var1} Z{var2}', sign0 * sign1 * sign2) hams = [ham0, ham1, ham2, ham3, ham4, ham5, ham6, ham7] ham = QubitOperator() for h in hams: ham += 1 / 8 * h return ham
def test_simulator_expectation_exception(sim): eng = MainEngine(sim, []) qureg = eng.allocate_qureg(3) op = QubitOperator('Z2') sim.get_expectation_value(op, qureg) op2 = QubitOperator('Z3') with pytest.raises(Exception): sim.get_expectation_value(op2, qureg) op3 = QubitOperator('Z1') + QubitOperator('X1 Y3') with pytest.raises(Exception): sim.get_expectation_value(op3, qureg)
def test_simulator_applyqubitoperator_exception(sim): eng = MainEngine(sim, []) qureg = eng.allocate_qureg(3) op = QubitOperator('Z2') sim.apply_qubit_operator(op, qureg) op2 = QubitOperator('Z3') with pytest.raises(Exception): sim.apply_qubit_operator(op2, qureg) op3 = QubitOperator('Z1') + QubitOperator('X1 Y3') with pytest.raises(Exception): sim.apply_qubit_operator(op3, qureg)
def test_sparse_matrix_linearity(self): identity = QubitOperator(()) zzzz = QubitOperator(tuple((i, 'Z') for i in range(4)), 1.0) sparse1 = get_sparse_operator(identity + zzzz) sparse2 = get_sparse_operator(identity, 4) + get_sparse_operator(zzzz) self.assertEqual(list(sparse1.data), [2] * 8) self.assertEqual(list(sparse1.indices), [0, 3, 5, 6, 9, 10, 12, 15]) self.assertEqual(list(sparse2.data), [2] * 8) self.assertEqual(list(sparse2.indices), [0, 3, 5, 6, 9, 10, 12, 15])
def test_transm_lower0(self): lowering = jordan_wigner(FermionOperator(((0, 0), ))) correct_operators_x = ((0, 'X'), ) correct_operators_y = ((0, 'Y'), ) qtermx = QubitOperator(correct_operators_x, 0.5) qtermy = QubitOperator(correct_operators_y, 0.5j) self.assertEqual(lowering.terms[correct_operators_x], 0.5) self.assertEqual(lowering.terms[correct_operators_y], 0.5j) self.assertTrue(lowering.isclose(qtermx + qtermy))
def test_transm_raise1(self): raising = jordan_wigner(FermionOperator(((1, 1), ))) correct_operators_x = ((0, 'Z'), (1, 'X')) correct_operators_y = ((0, 'Z'), (1, 'Y')) qtermx = QubitOperator(correct_operators_x, 0.5) qtermy = QubitOperator(correct_operators_y, -0.5j) self.assertEqual(raising.terms[correct_operators_x], 0.5) self.assertEqual(raising.terms[correct_operators_y], -0.5j) self.assertTrue(raising.isclose(qtermx + qtermy))
def test_or_gate_not_mutated(): saving_backend = DummyEngine(save_commands=True) eng = MainEngine(backend=saving_backend, engine_list=[]) qureg = eng.allocate_qureg(4) hamiltonian = QubitOperator("X0 Z3", 2) hamiltonian += QubitOperator("Y1", 0.5) correct_h = copy.deepcopy(hamiltonian) gate = te.TimeEvolution(2.1, hamiltonian) gate | qureg eng.flush() assert gate.hamiltonian.isclose(correct_h) assert gate.time == pytest.approx(2.1)
def test_qubitop2singlequbit(): num_qubits = 4 random_initial_state = [ 0.2 + 0.1 * x * cmath.exp(0.1j + 0.2j * x) for x in range(2**(num_qubits + 1)) ] rule_set = DecompositionRuleSet(modules=[qubitop2onequbit]) test_eng = MainEngine( backend=Simulator(), engine_list=[AutoReplacer(rule_set), InstructionFilter(_decomp_gates)], ) test_qureg = test_eng.allocate_qureg(num_qubits) test_ctrl_qb = test_eng.allocate_qubit() test_eng.flush() test_eng.backend.set_wavefunction(random_initial_state, test_qureg + test_ctrl_qb) correct_eng = MainEngine() correct_qureg = correct_eng.allocate_qureg(num_qubits) correct_ctrl_qb = correct_eng.allocate_qubit() correct_eng.flush() correct_eng.backend.set_wavefunction(random_initial_state, correct_qureg + correct_ctrl_qb) qubit_op_0 = QubitOperator("X0 Y1 Z3", -1.0j) qubit_op_1 = QubitOperator("Z0 Y1 X3", cmath.exp(0.6j)) qubit_op_0 | test_qureg with Control(test_eng, test_ctrl_qb): qubit_op_1 | test_qureg test_eng.flush() correct_eng.backend.apply_qubit_operator(qubit_op_0, correct_qureg) with Control(correct_eng, correct_ctrl_qb): Ph(0.6) | correct_qureg[0] Z | correct_qureg[0] Y | correct_qureg[1] X | correct_qureg[3] correct_eng.flush() for fstate in range(2**(num_qubits + 1)): binary_state = format(fstate, '0' + str(num_qubits + 1) + 'b') test = test_eng.backend.get_amplitude(binary_state, test_qureg + test_ctrl_qb) correct = correct_eng.backend.get_amplitude( binary_state, correct_qureg + correct_ctrl_qb) assert correct == pytest.approx(test, rel=1e-10, abs=1e-10) All(Measure) | correct_qureg + correct_ctrl_qb All(Measure) | test_qureg + test_ctrl_qb correct_eng.flush() test_eng.flush()
def test_yy(self): yy = QubitOperator(((2, 'Y'), (3, 'Y')), 2.) transmed_yy = reverse_jordan_wigner(yy) retransmed_yy = jordan_wigner(transmed_yy) expected1 = -(FermionOperator(((2, 1), ), 2.) + FermionOperator( ((2, 0), ), 2.)) expected2 = (FermionOperator(((3, 1), )) - FermionOperator(((3, 0), ))) expected = expected1 * expected2 self.assertTrue(yy.isclose(retransmed_yy)) self.assertTrue( normal_ordered(transmed_yy).isclose(normal_ordered(expected)))
def H_ansatz(N, B): ''' implement the mixer Hamiltonian given in the probelm parameters N: (int) number of allocated qubits B: (float) coupling coefficients return H:(QubitOperator) target hamiltonian ''' H = 0 * QubitOperator("") for i in range(N): H += B * QubitOperator("X" + str(i)) return (-1) * H
def test_xy(self): xy = QubitOperator(((4, 'X'), (5, 'Y')), -2.j) transmed_xy = reverse_jordan_wigner(xy) retransmed_xy = jordan_wigner(transmed_xy) expected1 = -2j * (FermionOperator(((4, 1), ), 1j) - FermionOperator( ((4, 0), ), 1j)) expected2 = (FermionOperator(((5, 1), )) - FermionOperator(((5, 0), ))) expected = expected1 * expected2 self.assertTrue(xy.isclose(retransmed_xy)) self.assertTrue( normal_ordered(transmed_xy).isclose(normal_ordered(expected)))
def test_recognize(): saving_backend = DummyEngine(save_commands=True) eng = MainEngine(backend=saving_backend, engine_list=[]) ctrl_qureg = eng.allocate_qureg(2) qureg = eng.allocate_qureg(2) with Control(eng, ctrl_qureg): QubitOperator("X0 Y1") | qureg with Control(eng, ctrl_qureg[0]): QubitOperator("X0 Y1") | qureg eng.flush() cmd0 = saving_backend.received_commands[4] cmd1 = saving_backend.received_commands[5] assert not qubitop2onequbit._recognize_qubitop(cmd0) assert qubitop2onequbit._recognize_qubitop(cmd1)
def test_yx(self): yx = QubitOperator(((0, 'Y'), (1, 'X')), -0.5) transmed_yx = reverse_jordan_wigner(yx) retransmed_yx = jordan_wigner(transmed_yx) expected1 = 1j * (FermionOperator(((0, 1), )) + FermionOperator( ((0, 0), ))) expected2 = -0.5 * (FermionOperator(((1, 1), )) + FermionOperator( ((1, 0), ))) expected = expected1 * expected2 self.assertTrue(yx.isclose(retransmed_yx)) self.assertTrue( normal_ordered(transmed_yx).isclose(normal_ordered(expected)))
def test_xx(self): xx = QubitOperator(((3, 'X'), (4, 'X')), 2.) transmed_xx = reverse_jordan_wigner(xx) retransmed_xx = jordan_wigner(transmed_xx) expected1 = (FermionOperator(((3, 1), ), 2.) - FermionOperator( ((3, 0), ), 2.)) expected2 = (FermionOperator(((4, 1), ), 1.) + FermionOperator( ((4, 0), ), 1.)) expected = expected1 * expected2 self.assertTrue(xx.isclose(retransmed_xx)) self.assertTrue( normal_ordered(transmed_xx).isclose(normal_ordered(expected)))
def _recognize_time_evolution_commuting_terms(cmd): """Recognize all TimeEvolution gates with >1 terms but which all commute.""" hamiltonian = cmd.gate.hamiltonian if len(hamiltonian.terms) == 1: return False id_op = QubitOperator((), 0.0) for term in hamiltonian.terms: test_op = QubitOperator(term, hamiltonian.terms[term]) for other in hamiltonian.terms: other_op = QubitOperator(other, hamiltonian.terms[other]) commutator = test_op * other_op - other_op * test_op if not commutator.isclose(id_op, rel_tol=1e-9, abs_tol=1e-9): return False return True
def test_decompose_commuting_terms(): saving_backend = DummyEngine(save_commands=True) def my_filter(self, cmd): if len(cmd.qubits[0]) <= 2 or isinstance(cmd.gate, ClassicalInstructionGate): return True return False rules = DecompositionRuleSet([te.rule_commuting_terms]) replacer = AutoReplacer(rules) filter_eng = InstructionFilter(my_filter) eng = MainEngine(backend=saving_backend, engine_list=[replacer, filter_eng]) qureg = eng.allocate_qureg(5) with Control(eng, qureg[3]): op1 = QubitOperator("X1 Y2", 0.7) op2 = QubitOperator("Y2 X4", -0.8) op3 = QubitOperator((), 0.6) TimeEvolution(1.5, op1 + op2 + op3) | qureg cmd1 = saving_backend.received_commands[5] cmd2 = saving_backend.received_commands[6] cmd3 = saving_backend.received_commands[7] found = [False, False, False] scaled_op1 = QubitOperator("X0 Y1", 0.7) scaled_op2 = QubitOperator("Y0 X1", -0.8) for cmd in [cmd1, cmd2, cmd3]: if (cmd.gate == Ph(-1.5 * 0.6) and cmd.qubits[0][0].id == qureg[1].id and cmd.control_qubits[0].id == qureg[3].id # 1st qubit of [1,2,4] ): found[0] = True elif (isinstance(cmd.gate, TimeEvolution) and cmd.gate.hamiltonian.isclose(scaled_op1) and cmd.gate.time == pytest.approx(1.5) and cmd.qubits[0][0].id == qureg[1].id and cmd.qubits[0][1].id == qureg[2].id and cmd.control_qubits[0].id == qureg[3].id): found[1] = True elif (isinstance(cmd.gate, TimeEvolution) and cmd.gate.hamiltonian.isclose(scaled_op2) and cmd.gate.time == pytest.approx(1.5) and cmd.qubits[0][0].id == qureg[2].id and cmd.qubits[0][1].id == qureg[4].id and cmd.control_qubits[0].id == qureg[3].id): found[2] = True assert all(found)
def test_or_not_enough_qubits(): saving_backend = DummyEngine(save_commands=True) eng = MainEngine(backend=saving_backend, engine_list=[]) qureg = eng.allocate_qureg(2) hamiltonian = QubitOperator("Z0 X3", 2) with pytest.raises(ValueError): te.TimeEvolution(2.1, hamiltonian) | qureg
def test_or_two_qubits_error(): saving_backend = DummyEngine(save_commands=True) eng = MainEngine(backend=saving_backend, engine_list=[]) qureg = eng.allocate_qureg(2) hamiltonian = QubitOperator("Z0", 2) with pytest.raises(TypeError): te.TimeEvolution(2.1, hamiltonian) | (qureg[0], qureg[1])
def error_operator(terms, series_order=2): """Determine the difference between the exact generator of unitary evolution and the approximate generator given by Trotter-Suzuki to the given order. Args: terms: a list of QubitTerms in the Hamiltonian to be simulated. series_order: the order at which to compute the BCH expansion. Only the second order formula is currently implemented (corresponding to Equation 9 of the paper). Returns: The difference between the true and effective generators of time evolution for a single Trotter step. Notes: follows Equation 9 of Poulin et al.'s work in "The Trotter Step Size Required for Accurate Quantum Simulation of Quantum Chemistry". """ if series_order != 2: raise NotImplementedError error_operator = QubitOperator() for beta in range(len(terms)): for alpha in range(beta + 1): for alpha_prime in range(beta): if not trivially_double_commutes(terms[alpha], terms[beta], terms[alpha_prime]): double_com = commutator(terms[alpha], commutator(terms[beta], terms[alpha_prime])) error_operator += double_com if alpha == beta: error_operator -= double_com / 2.0 return error_operator / 12.0