def exponential()

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