|
| 1 | +import numpy as np |
| 2 | + |
| 3 | +class QuantumFourierTransform: |
| 4 | + def __init__(self, n_qubits): |
| 5 | + self.n_qubits = n_qubits |
| 6 | + self.N = 2 ** n_qubits |
| 7 | + self.state = np.zeros(self.N, dtype=complex) |
| 8 | + |
| 9 | + def initialize_basis_state(self, index): |
| 10 | + """Set state to basis state |index⟩""" |
| 11 | + self.state = np.zeros(self.N, dtype=complex) |
| 12 | + self.state[index] = 1.0 |
| 13 | + |
| 14 | + def initialize_custom_state(self, state_vector): |
| 15 | + """Set state to custom normalized state vector""" |
| 16 | + assert len(state_vector) == self.N, "State vector length mismatch." |
| 17 | + norm = np.linalg.norm(state_vector) |
| 18 | + assert np.isclose(norm, 1.0), "State vector must be normalized." |
| 19 | + self.state = np.array(state_vector, dtype=complex) |
| 20 | + |
| 21 | + def initialize_superposition(self): |
| 22 | + """Create |+⟩^n superposition state""" |
| 23 | + self.state = np.ones(self.N, dtype=complex) / np.sqrt(self.N) |
| 24 | + def initialize_bell_pair(self): |
| 25 | + """2-qubit Bell state: (|00⟩ + |11⟩)/√2""" |
| 26 | + if self.n_qubits != 2: |
| 27 | + raise ValueError("Bell pair requires exactly 2 qubits.") |
| 28 | + self.state = np.zeros(self.N, dtype=complex) |
| 29 | + self.state[0] = 1 / np.sqrt(2) |
| 30 | + self.state[3] = 1 / np.sqrt(2) |
| 31 | + |
| 32 | + def initialize_ghz_state(self): |
| 33 | + """n-qubit GHZ state: (|00...0⟩ + |11...1⟩)/√2""" |
| 34 | + self.state = np.zeros(self.N, dtype=complex) |
| 35 | + self.state[0] = 1 / np.sqrt(2) |
| 36 | + self.state[-1] = 1 / np.sqrt(2) |
| 37 | + |
| 38 | + def hadamard(self): |
| 39 | + return np.array([[1, 1], [1, -1]]) / np.sqrt(2) |
| 40 | + |
| 41 | + def apply_single_qubit_gate(self, gate, target): |
| 42 | + I = np.eye(2) |
| 43 | + ops = [I] * self.n_qubits |
| 44 | + ops[target] = gate |
| 45 | + U = ops[0] |
| 46 | + for op in ops[1:]: |
| 47 | + U = np.kron(U, op) |
| 48 | + self.state = U @ self.state |
| 49 | + def apply_controlled_phase(self, control, target, theta): |
| 50 | + U = np.eye(self.N, dtype=complex) |
| 51 | + for i in range(self.N): |
| 52 | + b = format(i, f'0{self.n_qubits}b') |
| 53 | + if b[self.n_qubits - 1 - control] == '1' and b[self.n_qubits - 1 - target] == '1': |
| 54 | + U[i, i] *= np.exp(1j * theta) |
| 55 | + self.state = U @ self.state |
| 56 | + |
| 57 | + def swap_registers(self): |
| 58 | + perm = [int(format(i, f'0{self.n_qubits}b')[::-1], 2) for i in range(self.N)] |
| 59 | + self.state = self.state[perm] |
| 60 | + def apply_qft(self, inverse=False): |
| 61 | + for target in range(self.n_qubits): |
| 62 | + idx = self.n_qubits - 1 - target |
| 63 | + for offset in range(1, self.n_qubits - target): |
| 64 | + control = self.n_qubits - 1 - (target + offset) |
| 65 | + angle = np.pi / (2 ** offset) |
| 66 | + if inverse: |
| 67 | + angle *= -1 |
| 68 | + self.apply_controlled_phase(control, idx, angle) |
| 69 | + self.apply_single_qubit_gate(self.hadamard(), idx) |
| 70 | + self.swap_registers() |
| 71 | + def measure(self, shots=1024): |
| 72 | + probs = np.abs(self.state) ** 2 |
| 73 | + outcomes = np.random.choice(self.N, size=shots, p=probs) |
| 74 | + counts = {} |
| 75 | + for o in outcomes: |
| 76 | + bitstring = format(o, f'0{self.n_qubits}b') |
| 77 | + counts[bitstring] = counts.get(bitstring, 0) + 1 |
| 78 | + return counts |
| 79 | + |
| 80 | + def print_amplitudes(self, title="Quantum State"): |
| 81 | + print(f"\n{title}:") |
| 82 | + for i, amp in enumerate(self.state): |
| 83 | + print(f"|{i:0{self.n_qubits}b}>: {amp:.4f}") |
| 84 | + |
| 85 | +# --------------------------- |
| 86 | +# Example usage |
| 87 | +# --------------------------- |
| 88 | +if __name__ == "__main__": |
| 89 | + qft = QuantumFourierTransform(n_qubits=3) |
| 90 | + |
| 91 | + # Try different initializations |
| 92 | + qft.initialize_superposition() |
| 93 | + # qft.initialize_basis_state(5) |
| 94 | + # qft.initialize_custom_state(np.random.randn(8) + 1j*np.random.randn(8)) # normalize first |
| 95 | + # qft.initialize_ghz_state() |
| 96 | + |
| 97 | + qft.print_amplitudes("Initial State") |
| 98 | + |
| 99 | + qft.apply_qft() |
| 100 | + qft.print_amplitudes("After QFT") |
| 101 | + |
| 102 | + results = qft.measure(shots=1024) |
| 103 | + print("\nMeasurement Results:") |
| 104 | + for b, c in sorted(results.items()): |
| 105 | + print(f"{b}: {c}") |
| 106 | + |
| 107 | + qft.apply_qft(inverse=True) |
| 108 | + qft.print_amplitudes("After Inverse QFT") |
| 109 | + |
| 110 | + |
| 111 | + |
| 112 | +""" |
| 113 | +Quick Tips on Usage |
| 114 | +
|
| 115 | +
|
| 116 | +Superposition inputs (e.g. Hadamards to create |+\rangle^{\otimes n}) |
| 117 | +Custom state initialization (e.g. any normalized complex vector) |
| 118 | +Entangled state preparation (e.g. Bell states, GHZ states, etc.) |
| 119 | +
|
| 120 | +
|
| 121 | +
|
| 122 | +initialize_superposition() gives |+\rangle^{\otimes n} |
| 123 | +initialize_bell_pair() is for 2-qubit entangled states |
| 124 | +initialize_ghz_state() creates a GHZ state |
| 125 | +initialize_custom_state(vec) lets you pass any normalized state |
| 126 | +""" |
0 commit comments