def test_pauli_word_slice(self): self.assertEqual( PauliWord("XIYXZ")[1:5:2], PauliWord("IX") ) self.assertTrue( PauliWord("XIYXZ")[-1], PauliWord("Z") )
def test_factorization(self): factorization = pauli_word_exp_factorization(0.1, PauliWord("XIXXYZ")) self.assertEqual(factorization, [(-0.7853981633974483, PauliWord("IIIIXZ")), (-0.7853981633974483, PauliWord("IIIZZI")), (0.7853981633974483, PauliWord("IIIIXZ")), (-0.7853981633974483, PauliWord("IIZYII")), (0.1, PauliWord("XIYIII")), (0.7853981633974483, PauliWord("IIZYII")), (-0.7853981633974483, PauliWord("IIIIXZ")), (0.7853981633974483, PauliWord("IIIZZI")), (0.7853981633974483, PauliWord("IIIIXZ"))])
def test_symbolic_factorization(self): import sympy factorization = pauli_word_exp_factorization(sympy.Symbol("t"), PauliWord("XIXXYZ")) self.assertEqual(factorization, [(-0.7853981633974483, PauliWord("IIIIXZ")), (-0.7853981633974483, PauliWord("IIIZZI")), (0.7853981633974483, PauliWord("IIIIXZ")), (-0.7853981633974483, PauliWord("IIZYII")), (sympy.Symbol("t"), PauliWord("XIYIII")), (0.7853981633974483, PauliWord("IIZYII")), (-0.7853981633974483, PauliWord("IIIIXZ")), (0.7853981633974483, PauliWord("IIIZZI")), (0.7853981633974483, PauliWord("IIIIXZ"))])
def test_simulate(self): compressed_status = [True, False] rad = 0.75 pauli_word = PauliWord("XZYX") for initial_state in range(2**len(pauli_word)): results = [] for is_compressed in compressed_status: simulator = Simulator() circuit = Circuit() qubits = [GridQubit(i, j) for i in range(3) for j in range(3)] gate = PauliWordExpGate(rad, pauli_word) operation = gate.on(*qubits[0:len(gate.pauli_word)]) if is_compressed: circuit.append(operation) else: circuit.append(cirq.decompose(operation)) result = simulator.simulate( circuit, initial_state=initial_state ) # type: cirq.SimulationTrialResult # print(circuit) print(result.final_state) # print(cirq.unitary(circuit_compressed)) results.append(result) self.assertTrue( np.allclose(results[0].final_state, results[1].final_state, rtol=1e-4, atol=1e-5))
def test_pauli_word(self): for _, input_v in self.input_labels.items(): pauli_word = PauliWord(input_v) for check_k, check_v in self.check_labels.items(): self.assertEqual( check_v, getattr(pauli_word, check_k) )
def setUp(self) -> None: self.circuit_decomposed = Circuit() self.circuit_compressed = Circuit() self.qubits = [GridQubit(i, j) for i in range(3) for j in range(3)] self.gate = PauliWordExpGate(0.25, PauliWord("IXXZ")) self.operation = self.gate.on(*self.qubits[0:4]) self.circuit_decomposed.append(cirq.decompose(self.operation)) self.circuit_compressed.append(self.operation)
def test_factorization_special_case(self): """ This test case reveals the bug of `pauli_word_exp_factorization` when choosing `k`: use `sorted` instead of `list` to create list from `pauli_dict_form.keys()` (which is actually unordered). """ factorization = pauli_word_exp_factorization(1.2, PauliWord("ZZIIIIIZZ")) print(factorization) self.assertEqual(len(factorization), 5)
def test_symbolic_circuit(self): circuit = Circuit() qubits = [GridQubit(i, j) for i in range(3) for j in range(3)] gate = PauliWordExpGate(sympy.Symbol("t"), PauliWord("XXZX")) operation = gate.on(qubits[0], qubits[1], qubits[3], qubits[2]) circuit.append(cirq.decompose(operation)) circuit.append(operation) print(circuit)
def test_simplest_case(self): circuit = Circuit() qubits = [GridQubit(i, j) for i in range(3) for j in range(3)] gate = PauliWordExpGate(0.1, PauliWord("II")) circuit.append(gate.on(qubits[2], qubits[0])) # If use the following line instead, `print(circuit)` will show nothing: # circuit.append(cirq.decompose(gate.on(qubits[2], qubits[0]))) print(circuit) self.assertTrue(np.allclose(cirq.unitary(gate), np.identity(4)))
def test_for_random_matrix(self): num_qubits = 2**3 matrix = random_complex_matrix(num_qubits, num_qubits) expansion = pauli_expansion_for_any_matrix(matrix) _matrix = 0.0 for term, coeff in expansion.items(): _matrix += coeff * PauliWord(term).sparray.toarray() self.assertTrue(np.allclose(_matrix, matrix))
def test_array(self): self.assertTrue(np.allclose( PauliWord("XIY").sparray.toarray(), np.array([ [0, 0, 0, 0, 0, -1j, 0, 0], [0, 0, 0, 0, 1j, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, -1j], [0, 0, 0, 0, 0, 0, 1j, 0], [0, -1j, 0, 0, 0, 0, 0, 0], [1j, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, -1j, 0, 0, 0, 0], [0, 0, 1j, 0, 0, 0, 0, 0] ]) ))
def test_diagram(self): qubits = [cirq.GridQubit(i, j) for i in range(2) for j in range(2)] circuit = cirq.circuits.Circuit() circuit.append( cirq.MatrixGate( splinalg.expm(1j * 0.1 * PauliWord("XX").sparray.toarray()))( *qubits[0:2])) print(circuit) self.assertEqual( str(circuit), " ┌ ┐\n" " │0.995+0.j 0. +0.j 0. +0.j 0. +0.1j│\n" "(0, 0): ───│0. +0.j 0.995+0.j 0. +0.1j 0. +0.j │───\n" " │0. +0.j 0. +0.1j 0.995+0.j 0. +0.j │\n" " │0. +0.1j 0. +0.j 0. +0.j 0.995+0.j │\n" " └ ┘\n" " │\n" "(0, 1): ───#2──────────────────────────────────────────────")
def test_effective_len(self): self.assertEqual( PauliWord("XIYXZ").effective_len, 4 )
def test_identity(self): self.assertEqual( PauliWord("I" * 10).qubit_operator_str, "" )
def test_unitary(self): self.assertTrue( np.allclose( cirq.unitary(self.gate), splinalg.expm(-1.0j * 0.25 * PauliWord("IXXZ").sparray.toarray())))
class OperationGradTest(unittest.TestCase): @ddt.unpack @ddt.data( # d[rx(rad)] / d[rad] = d[e^{-i X rad / 2}] / d[rad] # = -i/2 X e^{-i X rads / 2} # = -i/2 X rx(rad) [rx(rad).on(q0), rad, LCO({ (rx(rad).on(q0), X(q0)): -0.5j })], # d[XPow(e, s)] / d[e] = i pi {s + 1/2 (I - X)} XPow(e, s) [XPowGate(exponent=rad, global_shift=0.3).on(q0), rad, LCO({ (XPowGate(exponent=rad, global_shift=0.3).on(q0),): 1.0j * np.pi * (0.3 + 0.5), (X(q0), XPowGate(exponent=rad, global_shift=0.3).on(q0)): -0.5j * np.pi })], # d[e^{-i A0 A1 rad}] / d[rad] = -i A0 A1 e^{-i A0 A1 rad} [TwoPauliExpGate("X", "Z", rad).on(q0, q1), rad, LCO({ (TwoPauliExpGate("X", "Z", rad).on(q0, q1), X(q0), Z(q1)): -1.0j })], # d[exp(i pi rad)] / d[rad] = i pi exp(i pi rad) [GlobalPhaseGate(rad ** 2).on(q0), rad, LCO({ (GlobalPhaseGate(rad ** 2).on(q0),): 2.0j * sympy.pi * rad })], # d[e^{−i t P}] / d[t] = -i P e^{−i t P} [PauliWordExpGate(rad ** 2, PauliWord("ZYX")).on(q0, q1, q2), rad, LCO({ (PauliWordExpGate(rad ** 2, PauliWord("ZYX")).on(q0, q1, q2), Z(q0), Y(q1), X(q2)): -2.0j * rad })], # d[rx(rad) ⊗ rz(rad)] / d[rad] = -i/2 { X rx(rad) ⊗ rz(rad) # + rx(rad) ⊗ Z rz(rad) } [GateBlock( TwoRotsGenerator() ).on(q0, q1), rad, -0.5j * LCO({ (rz(rad).on(q1), rx(rad).on(q0), X(q0)): 1, (rz(rad).on(q1), rx(rad).on(q0), Z(q1)): 1 })], # d[CRx(rad)] / d[rad] = |1><1| ⊗ d[Rx(rad)]/d[rad] # = -i/2 |1><1| ⊗ X Rx(rad) # = -i/4 { C[X Rx(rad)] - C[-X Rx(rad)] } [cirq.ControlledGate(rx(rad), num_controls=1).on(q0, q1), rad, -1.0j / 4 * LCO({ (ControlledGate(X).on(q0, q1), ControlledGate(rx(rad)).on(q0, q1)): 1, (ControlledGate(X).on(q0, q1), ControlledGate(rx(rad)).on(q0, q1), ControlledGate(GlobalPhaseGate(1)).on(q0, q1)): -1 })], # d[CCRz(rad)] / d[rad] = |1><1| ⊗ |1><1| ⊗ d[Rz(rad)]/d[rad] # = -i/4 { CC[Z Rz(rad)] - CC[-Z Rz(rad)] } [ControlledGate( ControlledGate( cirq.rz(rad) ) ).on(q0, q1, q2), rad, -1.0j / 4 * LCO({ (ControlledGate(ControlledGate(Z)).on(q0, q1, q2), ControlledGate(ControlledGate(rz(rad))).on(q0, q1, q2)): 1, (ControlledGate(ControlledGate(Z)).on(q0, q1, q2), ControlledGate(ControlledGate(rz(rad))).on(q0, q1, q2), ControlledGate(ControlledGate(GlobalPhaseGate(1))).on(q0, q1, q2)): -1 })], # d[U(rad)] = -i/2 U(rad) X.on(q0) - i/4 { U+(rad) - U-(rad) } [u.on(q0, q1, q2, q3, q4), rad, LCO({ (X(q0), u.on(q0, q1, q2, q3, q4),): -0.5j, # order matters! (u_positive.on(q0, q1, q2, q3, q4),): -0.25j, (u_negative.on(q0, q1, q2, q3, q4),): +0.25j })] ) def test_grad(self, op, param, grad_lco): print("[Grad of {}]".format(op)) grad = op_grad(op, param) if isinstance(grad, GradNotImplemented): print("The grad of {} is not implemented.".format(grad.operation)) return for _op_series, coeff in grad.items(): _circuit = cirq.Circuit() _circuit.append(_op_series) print("Coefficient: {}".format(coeff)) print(_circuit, "\n") random_rad = typing.cast(float, np.random.rand()) param_resolver = {"rad": random_rad} # param_resolver = {"rad": 0} test_lco_identical_with_simulator( cirq.resolve_parameters(grad, param_resolver), cirq.resolve_parameters(grad_lco, param_resolver), self )
def test_dict_form(self): self.assertEqual( PauliWord("XIYXZ").dict_form, {0: "X", 2: "Y", 3: "X", 4: "Z"} )
def test_resize(self): word = PauliWord("XIYXZ") self.assertEqual( PauliWord(word, length=3), PauliWord("XIY") ) self.assertEqual( PauliWord(word.fullstr, length=3), PauliWord("XIY") ) self.assertEqual( PauliWord(word.qubit_operator_str, length=3), PauliWord("XIY") ) self.assertEqual( PauliWord(word, length=8), PauliWord("XIYXZIII") ) self.assertEqual( PauliWord(word.fullstr, length=8), PauliWord("XIYXZIII") ) self.assertEqual( PauliWord(word.qubit_operator_str, length=8), PauliWord("XIYXZIII") )
def pauli_word_exp_factorization( coefficient: typing.Union[float, sympy.Basic], pauli_word: PauliWord ) -> typing.List[typing.Tuple[typing.Union[float, sympy.Basic], PauliWord]]: """ Factorize the exponentiation of Pauli word P $$ e^{−i t P} $$ (where t is the coefficient) with formula: $$ e^{−i t P} = e^{i (π/4) wk'' P2} e^{−i t P1 wk'} e^{−i (π/4) wk'' P2}, $$ where wk, P1 and P2 satisfy $P = P1 wk P2$. The subscript of $wk$ denotes that this elementary Pauli operator corresponds to the kth qubit. The word $P1 (P2)$ contains all qubit indices that are strictly greater (lower) than k. wk' and wk'' are the other two Pauli operators which satisfy $$ [ w', w'' ] = 2 i w. $$ :param coefficient: float coefficient t in the exponential. :param pauli_word: PauliWord the Pauli word P in the exponential. :return: the factorized form of $e^{−i t P}$. For example, if $$ e^{−i t P} = e^{−i tn Pn} ... e^{−i t1 P1} e^{−i t0 P0}, $$ then the returned will be like: [ (tn, Pn), ... (t0, P0), ]. Note that 0 <= Pi.effective_len <= 2. """ if pauli_word.effective_len == 0: return [] elif pauli_word.effective_len < 3: return [(coefficient, pauli_word)] # choose k pauli_dict_form = pauli_word.dict_form k = sorted(pauli_dict_form.keys())[len(pauli_dict_form) // 2] p1, wk, p2 = [pauli_word[:k], pauli_word[k], pauli_word[k + 1:]] i1, i2 = map(lambda word: PauliWord("I" * len(word)), [p1, p2]) wk1, wk2 = Pauli(wk.word).get_the_other_two_paulis() _factorized = pauli_word_exp_factorization(coefficient=sympy.Symbol("t"), pauli_word=PauliWord.join( i1, wk2, p2)) returned = [(float(c.subs({"t": -np.pi / 4})), w) if isinstance(c, sympy.Basic) else (c, w) for c, w in _factorized] returned.extend( pauli_word_exp_factorization(coefficient=coefficient, pauli_word=PauliWord.join(p1, wk1, i2))) returned.extend([(float(c.subs({"t": np.pi / 4})), w) if isinstance(c, sympy.Basic) else (c, w) for c, w in _factorized]) # returned = pauli_word_exp_factorization( # coefficient=-np.pi / 4, # pauli_word=PauliWord.join(i1, wk2, p2) # ) # # returned.extend(pauli_word_exp_factorization( # coefficient=coefficient, # pauli_word=PauliWord.join(p1, wk1, i2) # )) # # returned.extend(pauli_word_exp_factorization( # coefficient=np.pi / 4, # pauli_word=PauliWord.join(i1, wk2, p2) # )) return returned