Calculate the Fubini–Study metric between elements of a Hilbert space.

    The Fubini–Study metric is a distance measure between vectors in a
    projective Hilbert space. For gates this space is the Hilbert space of
    operators induced by the Hilbert-Schmidt inner product.
    For 1-qubit rotation gates, RX, RY and RZ, this is half the angle (theta)
    in the Bloch sphere.

    The Fubini–Study metric between states is equal to the Burr angle
    between pure states.

def fubini_study_angle(vec0: QubitVector, vec1: QubitVector) -> bk.BKTensor:
    """Calculate the Fubini–Study metric between elements of a Hilbert space.

    The Fubini–Study metric is a distance measure between vectors in a
    projective Hilbert space. For gates this space is the Hilbert space of
    operators induced by the Hilbert-Schmidt inner product.
    For 1-qubit rotation gates, RX, RY and RZ, this is half the angle (theta)
    in the Bloch sphere.

    The Fubini–Study metric between states is equal to the Burr angle
    between pure states.
    """
    if vec0.rank != vec1.rank or vec0.qubit_nb != vec1.qubit_nb:
        raise ValueError('Incompatibly vectors. Qubits and rank must match')

    vec1 = vec1.permute(vec0.qubits)  # Make sure qubits in same order

    t0 = vec0.tensor
    t1 = vec1.tensor
    hs01 = bk.inner(t0, t1)  # Hilbert-Schmidt inner product
    hs00 = bk.inner(t0, t0)
    hs11 = bk.inner(t1, t1)
    ratio = bk.absolute(hs01) / bk.sqrt(bk.absolute(hs00*hs11))
    ratio = bk.minimum(ratio, bk.fcast(1.))  # Compensate for rounding errors.
    return bk.arccos(ratio)