Tensorflow 2.0 example. Given an arbitrary one-qubit gate, use
    gradient descent to find corresponding parameters of a universal ZYZ
    gate.

def fit_zyz(target_gate):
    """
    Tensorflow 2.0 example. Given an arbitrary one-qubit gate, use
    gradient descent to find corresponding parameters of a universal ZYZ
    gate.
    """

    steps = 1000

    dev = '/gpu:0' if bk.DEVICE == 'gpu' else '/cpu:0'

    with tf.device(dev):
        t = tf.Variable(tf.random.normal([3]))

        def loss_fn():
            """Loss"""
            gate = qf.ZYZ(t[0], t[1], t[2])
            ang = qf.fubini_study_angle(target_gate.vec, gate.vec)
            return ang

        opt = tf.optimizers.Adam(learning_rate=0.001)
        opt.minimize(loss_fn, var_list=[t])

        for step in range(steps):
            opt.minimize(loss_fn, var_list=[t])
            loss = loss_fn()
            print(step, loss.numpy())
            if loss < 0.01:
                break
        else:
            print("Failed to coverge")

    return bk.evaluate(t)