Skip to content
Back to Blog
1 min read

Quantum Computing Basics: Understanding Qubits and Gates

I wrote “Quantum Computing Basics: Understanding Qubits and Gates” to share practical, production-minded guidance on this topic.

Classical vs Quantum Bits

Classical computers use bits that are either 0 or 1. Quantum computers use qubits that can exist in superposition - a combination of both states simultaneously.

# Classical bit representation
classical_bit = 0  # or 1

# Quantum state representation (using numpy)
import numpy as np

# |0⟩ state vector
state_zero = np.array([1, 0])

# |1⟩ state vector
state_one = np.array([0, 1])

# Superposition state: |+⟩ = (|0⟩ + |1⟩) / √2
state_plus = np.array([1/np.sqrt(2), 1/np.sqrt(2)])

# Probability of measuring |0⟩
prob_zero = np.abs(state_plus[0])**2  # 0.5

# Probability of measuring |1⟩
prob_one = np.abs(state_plus[1])**2   # 0.5

Quantum Gates as Matrices

Quantum gates are represented as unitary matrices:

import numpy as np

# Pauli-X gate (quantum NOT)
X = np.array([[0, 1],
              [1, 0]])

# Pauli-Y gate
Y = np.array([[0, -1j],
              [1j, 0]])

# Pauli-Z gate
Z = np.array([[1, 0],
              [0, -1]])

# Hadamard gate (creates superposition)
H = np.array([[1, 1],
              [1, -1]]) / np.sqrt(2)

# Phase gate
S = np.array([[1, 0],
              [0, 1j]])

# T gate (π/8 gate)
T = np.array([[1, 0],
              [0, np.exp(1j * np.pi / 4)]])

# Apply gate to qubit state
def apply_gate(gate, state):
    return np.dot(gate, state)

# Example: Apply Hadamard to |0⟩
state_zero = np.array([1, 0])
after_hadamard = apply_gate(H, state_zero)
print(f"After Hadamard: {after_hadamard}")
# Output: [0.707, 0.707] - superposition!

Multi-Qubit Systems

import numpy as np
from functools import reduce

# Tensor product for multi-qubit states
def tensor_product(a, b):
    return np.kron(a, b)

# Two-qubit state |00⟩
state_00 = tensor_product(np.array([1, 0]), np.array([1, 0]))
# Result: [1, 0, 0, 0]

# Two-qubit state |01⟩
state_01 = tensor_product(np.array([1, 0]), np.array([0, 1]))
# Result: [0, 1, 0, 0]

# CNOT gate (Controlled-NOT)
CNOT = np.array([
    [1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 0, 1],
    [0, 0, 1, 0]
])

# Create Bell state |Φ+⟩ = (|00⟩ + |11⟩) / √2
def create_bell_state():
    # Start with |00⟩
    state = np.array([1, 0, 0, 0])

    # Apply Hadamard to first qubit
    H_I = tensor_product(H, np.eye(2))  # H ⊗ I
    state = np.dot(H_I, state)

    # Apply CNOT
    state = np.dot(CNOT, state)

    return state

bell_state = create_bell_state()
print(f"Bell state: {bell_state}")
# Output: [0.707, 0, 0, 0.707] representing (|00⟩ + |11⟩)/√2

Quantum Circuit Simulation

Build a simple quantum simulator:

class QuantumSimulator:
    def __init__(self, num_qubits):
        self.num_qubits = num_qubits
        self.state = np.zeros(2**num_qubits, dtype=complex)
        self.state[0] = 1  # Initialize to |0...0⟩

    def apply_single_qubit_gate(self, gate, target_qubit):
        """Apply a single-qubit gate to the specified qubit."""
        n = self.num_qubits

        # Build the full operator using tensor products
        if target_qubit == 0:
            full_gate = gate
        else:
            full_gate = np.eye(2)

        for i in range(1, n):
            if i == target_qubit:
                full_gate = np.kron(full_gate, gate)
            else:
                full_gate = np.kron(full_gate, np.eye(2))

        self.state = np.dot(full_gate, self.state)

    def apply_cnot(self, control, target):
        """Apply CNOT gate."""
        n = self.num_qubits
        dim = 2**n
        cnot_matrix = np.zeros((dim, dim), dtype=complex)

        for i in range(dim):
            bits = [(i >> j) & 1 for j in range(n)]
            if bits[control] == 1:
                bits[target] ^= 1
            j = sum(b << k for k, b in enumerate(bits))
            cnot_matrix[j, i] = 1

        self.state = np.dot(cnot_matrix, self.state)

    def measure(self):
        """Perform measurement, return result and collapse state."""
        probabilities = np.abs(self.state)**2
        result = np.random.choice(len(self.state), p=probabilities)

        # Collapse to measured state
        self.state = np.zeros_like(self.state)
        self.state[result] = 1

        return format(result, f'0{self.num_qubits}b')

    def measure_many(self, shots=1000):
        """Run circuit multiple times and return measurement statistics."""
        original_state = self.state.copy()
        results = {}

        for _ in range(shots):
            self.state = original_state.copy()
            outcome = self.measure()
            results[outcome] = results.get(outcome, 0) + 1

        return results


# Example: Create and measure Bell state
sim = QuantumSimulator(2)
sim.apply_single_qubit_gate(H, 0)  # Hadamard on qubit 0
sim.apply_cnot(0, 1)               # CNOT with control=0, target=1

# Measure multiple times
results = sim.measure_many(1000)
print("Bell state measurements:")
for outcome, count in sorted(results.items()):
    print(f"  |{outcome}⟩: {count/10:.1f}%")
# Expected: ~50% |00⟩, ~50% |11⟩

Bloch Sphere Visualization

Visualize qubit states:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np

def state_to_bloch(state):
    """Convert quantum state to Bloch sphere coordinates."""
    # state = [α, β] where |ψ⟩ = α|0⟩ + β|1⟩
    alpha, beta = state

    # Calculate Bloch sphere angles
    theta = 2 * np.arccos(np.abs(alpha))
    phi = np.angle(beta) - np.angle(alpha)

    # Convert to Cartesian coordinates
    x = np.sin(theta) * np.cos(phi)
    y = np.sin(theta) * np.sin(phi)
    z = np.cos(theta)

    return x, y, z

def plot_bloch_sphere(states, labels=None):
    """Plot states on Bloch sphere."""
    fig = plt.figure(figsize=(10, 10))
    ax = fig.add_subplot(111, projection='3d')

    # Draw sphere
    u = np.linspace(0, 2 * np.pi, 100)
    v = np.linspace(0, np.pi, 50)
    x = np.outer(np.cos(u), np.sin(v))
    y = np.outer(np.sin(u), np.sin(v))
    z = np.outer(np.ones(np.size(u)), np.cos(v))
    ax.plot_surface(x, y, z, alpha=0.1, color='blue')

    # Draw axes
    ax.quiver(0, 0, 0, 1.5, 0, 0, color='r', arrow_length_ratio=0.1)
    ax.quiver(0, 0, 0, 0, 1.5, 0, color='g', arrow_length_ratio=0.1)
    ax.quiver(0, 0, 0, 0, 0, 1.5, color='b', arrow_length_ratio=0.1)

    # Plot states
    colors = plt.cm.rainbow(np.linspace(0, 1, len(states)))
    for i, state in enumerate(states):
        x, y, z = state_to_bloch(state)
        label = labels[i] if labels else f'State {i}'
        ax.scatter([x], [y], [z], color=colors[i], s=100, label=label)
        ax.quiver(0, 0, 0, x, y, z, color=colors[i], arrow_length_ratio=0.1)

    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')
    ax.legend()
    plt.title('Bloch Sphere Representation')
    plt.show()

# Example states
states = [
    np.array([1, 0]),                      # |0⟩
    np.array([0, 1]),                      # |1⟩
    np.array([1, 1]) / np.sqrt(2),         # |+⟩
    np.array([1, -1]) / np.sqrt(2),        # |-⟩
    np.array([1, 1j]) / np.sqrt(2),        # |i⟩
]
labels = ['|0⟩', '|1⟩', '|+⟩', '|-⟩', '|i⟩']

plot_bloch_sphere(states, labels)

Summary

Quantum computing fundamentals:

  • Qubits exist in superposition states
  • Quantum gates are unitary matrices
  • Multi-qubit systems use tensor products
  • Measurement collapses superposition
  • Entanglement creates correlated states

These concepts form the foundation for quantum algorithms.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.