in tensorflow_quantum/python/util.py [0:0]
def exponential(operators, coefficients=None):
"""Return a Cirq circuit with exponential operator forms.
Construct an exponential form of given `operators` and `coefficients`.
Operators to be exponentiated are specified in `operators` as
`cirq.PauliSum` or `cirq.PauliString`. Parameters are given by
`coefficients`.
Note that only operators whose standard representations consist of terms
which all commute can be exponentiated. This allows use of the identity
exp(A+B+...) = exp(A)exp(B)... else there would need to be automatic
handling of Trotterization and convergence, which is not supported yet.
Args:
operators: Python `list` or `tuple` of `cirq.PauliSum` or
`cirq.PauliString` objects to be exponentiated.
Here are simple examples.
Let q = cirq.GridQubit(0, 0)
E.g. operator = 0.5 * X(q) -> exp(-i * 0.5 * X(q))
operator = 0.5 * cirq.PauliString({q: cirq.I})
-> exp(-i * 0.5)*np.eye(2)
Be careful of the negation and the PauliString of the identity gate.
coefficients: (Optional) Python `list` of Python `str`, `float` or
`sympy.Symbol` object of parameters. Defaults to None, then all
coefficients of `operators` are set to 1.0.
Returns:
A `cirq.Circuit` containing exponential form of given `operators`
and `coefficients`.
Raises:
TypeError: If `operators` (or its terms) is/are of an invalid type.
"""
# Ingest operators.
if not isinstance(operators, (list, tuple)):
raise TypeError("operators is not a list of operators.")
if not all(
isinstance(x, (cirq.PauliSum, cirq.PauliString))
for x in operators):
raise TypeError("Each element in operators must be a "
"cirq.PauliSum or cirq.PauliString object.")
# Ingest coefficients.
if coefficients is None:
coefficients = [1.0 for _ in operators]
if not isinstance(coefficients, (list, tuple, np.ndarray)):
raise TypeError("coefficients is not a list of coefficients.")
if not all(isinstance(x, (str, sympy.Symbol, float)) for x in coefficients):
raise TypeError("Each element in coefficients"
" must be a float or a string or sympy.Symbol.")
if len(coefficients) != len(operators):
raise ValueError("the number of operators should be the same as that "
"of coefficients. Got {} operators and {} coefficients"
"".format(len(operators), len(coefficients)))
coefficients = [
sympy.Symbol(s) if isinstance(s, str) else s
for i, s in enumerate(coefficients)
]
circuit = cirq.Circuit()
operators = [
cirq.PauliSum.from_pauli_strings(ps) if isinstance(
ps, cirq.PauliString) else ps for ps in operators
]
qubit_set = {q for psum in operators for q in psum.qubits}
identity_ref_qubit = cirq.GridQubit(0, 0)
if len(qubit_set) > 0:
identity_ref_qubit = sorted(list(qubit_set))[0]
for param, pauli_sum in zip(coefficients, operators):
if isinstance(pauli_sum, cirq.PauliSum):
check_commutability(pauli_sum)
for op in pauli_sum:
if abs(op.coefficient.imag) > 1e-9:
raise TypeError('exponential only supports real '
'coefficients: got '
'{}'.format(op.coefficient))
# Create a circuit with exponentiating `op` with param
c = op.coefficient.real
if len(op.gate.pauli_mask) == 0:
# If given gate_op is identity.
circuit += exp_identity(param, c, identity_ref_qubit)
continue
# Where to perform the Rz gate based on difficulty of CNOT's
# TODO(jaeyoo): will write a super duper optimization on this.
# currently going on HIGHEST-indexed qubit.
k = op.qubits[-1]
# Set of gates to convert all X's and Y's -> Z's.
u, u_dagger = _many_clifford_to_many_z(op)
# Set of gates to convert many Z's into a single Z using CNOTs.
w, w_dagger = _many_z_to_single_z(k, op)
# cirq.rz(2*theta) = exp(-i*0.5*(2*theta)*Z) == exp(-i*theta*Z)
# focal point of the CNOT ladder.
exp_circuit = u + w + [cirq.rz(2 * param * c)(k)
] + w_dagger + u_dagger
circuit += cirq.Circuit(exp_circuit)
return circuit