Site Tools


qubit-gates-in-c

Table of Contents

Qubit gate

Simulation

In C99, a header <complex.h> is provided that implements complex numbers.

State

As we've seen, a qubit is a two-state configuration with complex probability amplitudes distributed between the two states.
This gives us the following implementation of a single qubit: $$\lvert\psi\rangle = \alpha\lvert 0\rangle + \beta\lvert 1\rangle,\qquad \alpha,\beta\in\mathbb C$$

#include <complex.h>
 
typedef struct {
    double complex a; // alpha
    double complex b; // beta
} qubit_t;

Norm (Born rule)

$$\lVert\psi\lVert^2 = |\alpha|^2 + |\beta|^2 = \alpha\bar{\alpha} + \beta\bar{\beta}$$

double qubit_norm2(qubit_t psi)
{
    return creal(psi.a * conj(psi.a) + psi.b * conj(psi.b))
}

Normalization

$$\lvert\psi\rangle\rightarrow\frac{\lvert\psi\rangle}{\sqrt{\langle\psi\lvert\psi\rangle}}$$

qubit_t qubit_normalize(qubit_t psi)
{
    double norm = sqrt(qubit_norm2(psi));
    return norm == 0.0 ? psi : { psi.a / norm, psi.b / norm };
}

Inner product

$$\langle\phi\lvert\psi\rangle = \overline{\phi_\alpha}\psi_\alpha + \overline{\phi_\beta}\psi_\beta$$

double complex qubit_inner(qubit_t phi, qubit_t psi)
{
    return conj(phi.a) * psi.a + conj(phi.b) * psi.b;
}

Measurement probabilities (Born rule)

$$P(0) = |\alpha|^2 = \alpha\bar{\alpha}$$

double qubit_prob0(qubit_t psi)
{
    return creal(psi.a * conj(psi.a));
}

$$P(1) = |\beta|^2 = \beta\bar{\beta}$$

double qubit_prob1(qubit_t psi)
{
    return creal(psi.b * conj(psi.b));
}

Projective measurement (collapse)

$$\lvert\psi\rangle\rightarrow\frac{P_i\lvert\psi\rangle}{\sqrt{\langle\psi\lvert P_i\lvert\psi\rangle}}$$ $$P_0 = \lvert 0\rangle\langle 0\lvert$$ $$P_1 = \lvert 1\rangle\langle 1\lvert$$

#define C_ONE  (1.0 + I * 0.0)
#define C_ZERO (0.0 + I * 0.0)
qubit_t qubit_measure(qubit_t psi)
{
    double r = (double)rand() / RAND_MAX;
    if (r < qubit_prob0(psi)) {
        return { C_ONE, C_ZERO };
    } else {
        return { C_ZERO, C_ONE };
    }
}

Linear operator (unitary gate)

$$U = \begin{pmatrix}u_{00} & u_{01} \\ u_{10} & u_{11} \end{pmatrix}$$ $$\lvert\psi'\rangle = U\lvert\psi\rangle$$

typedef struct {
    double complex u00, u01;
    double complex u10, u11;
} gate2_t;
 
qubit_t gate2_apply(gate2_t U, qubit_t psi)
{
    return (qubit_t){
        U.u00 * psi.a + U.u01 * psi.b,
        U.u10 * psi.a + U.u11 * psi.b
    };
}

Pauli-X (bit flip)

Pauli-X operator swaps amplitudes. It's a $180^\deg$ rotation around the X-axis of the Bloch sphere. $$X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix}$$

gate2_t gate_X(void)
{
    return (gate2_t){
        0, 1,
        1, 0
    };
}

Pauli-Z (phase flip)

Pauli-Z operator flips the relative phase. Probabilities remain unchanged, but interference patterns change. $$Z = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix}$$

gate2_t gate_Z(void)
{
    return (gate2_t){
        1, 0,
        0, -1
    };
}

Hadamard (superposition generator)

Hadamard gate mixes the amplitudes evenly. If a qubit is in a pure state $\lvert\psi\rangle = \lvert 0\rangle$, after Hadamard states are evenly mixed $1/\sqrt{2}\cdot(\lvert 0\rangle + \lvert 1\rangle)$. If you apply Hadamard again, you return back to $\lvert\psi\rangle$, so Hadamard is its own inverse. This is the gate that enables interference-based algorithms. $$H = \frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}$$

gate2_t gate_H(void)
{
    double s = 1.0 / sqrt(2.0);
    return (gate2_t){
        s,  s,
        s, -s
    };
}

Z-axis rotation

This connects the Schrodinger equation with gate logic.

$$R_z(\theta) = e^{-i\theta\sigma_z/2} = \begin{pmatrix} e^{-i\theta / 2} & 0 \\ 0 & e^{i\theta/2} \end{pmatrix}$$

gate2_t gate_Rz(double theta)
{
    double complex e_minus = cexp(-I * theta / 2.0);
    double complex e_plus  = cexp( I * theta / 2.0);
 
    return (gate2_t){
        e_minus, 0,
        0,       e_plus
    };
}

Global phase invariance

$$\lvert\psi\rangle \sim e^{i\phi}\lvert\psi\rangle$$

qubit_t qubit_global_phase(qubit_t psi, double phi)
{
    double complex phase = cexp(I * phi);
    psi.a *= phase;
    psi.b *= phase;
    return psi;
}

Bloch sphere parametrization

$$\lvert\psi\rangle = \cos(\theta / 2)\cdot\lvert 0\rangle + e^{i\phi}\sin(\theta/2)\cdot\lvert 1 \rangle$$

void qubit_to_bloch(qubit_t psi, double *theta, double *phi)
{
    psi = qubit_normalize(psi);
 
    double a_mag = cabs(psi.a);
    *theta = 2.0 * acos(a_mag);
 
    double arg_a = carg(psi.a);
    double arg_b = carg(psi.b);
    *phi = arg_b - arg_a;
}
qubit-gates-in-c.txt ยท Last modified: (external edit)