Skip to content

Commit bffbabc

Browse files
committed
update
1 parent 45b352a commit bffbabc

File tree

3 files changed

+330
-0
lines changed

3 files changed

+330
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import numpy as np
2+
3+
def hadamard():
4+
return np.array([[1, 1], [1, -1]]) / np.sqrt(2)
5+
6+
def apply_single_qubit_gate(state, gate, target, n_qubits):
7+
"""Applies a single-qubit gate to the target qubit."""
8+
I = np.eye(2)
9+
ops = [I] * n_qubits
10+
ops[target] = gate
11+
U = ops[0]
12+
for op in ops[1:]:
13+
U = np.kron(U, op)
14+
return U @ state
15+
16+
def apply_controlled_phase(state, control, target, theta, n_qubits):
17+
"""Applies a controlled phase rotation gate."""
18+
size = 2 ** n_qubits
19+
U = np.eye(size, dtype=complex)
20+
for i in range(size):
21+
bin_str = format(i, f'0{n_qubits}b')
22+
if bin_str[n_qubits - 1 - control] == '1' and bin_str[n_qubits - 1 - target] == '1':
23+
U[i, i] *= np.exp(1j * theta)
24+
return U @ state
25+
26+
def swap_registers(state, n_qubits):
27+
"""Swap qubit order (bit-reversal permutation)."""
28+
perm = [int(format(i, f'0{n_qubits}b')[::-1], 2) for i in range(2 ** n_qubits)]
29+
return state[perm]
30+
31+
def qft_step_by_step(state, n_qubits, inverse=False):
32+
"""Performs (inverse) QFT using gate decomposition."""
33+
for target in range(n_qubits):
34+
idx = n_qubits - 1 - target # Apply to qubits in reversed order
35+
# Controlled phase rotations
36+
for control_offset in range(1, n_qubits - target):
37+
control = n_qubits - 1 - (target + control_offset)
38+
angle = np.pi / (2 ** control_offset)
39+
if inverse:
40+
angle *= -1
41+
state = apply_controlled_phase(state, control, idx, angle, n_qubits)
42+
# Hadamard
43+
state = apply_single_qubit_gate(state, hadamard(), idx, n_qubits)
44+
# Swap qubits unless inverse and user wants to avoid it
45+
return swap_registers(state, n_qubits)
46+
47+
def measure_state(state, shots=1024):
48+
"""Measure the quantum state multiple times to simulate outcomes."""
49+
probs = np.abs(state) ** 2
50+
outcomes = np.random.choice(len(probs), size=shots, p=probs)
51+
counts = {}
52+
for o in outcomes:
53+
b = format(o, f'0{int(np.log2(len(state)))}b')
54+
counts[b] = counts.get(b, 0) + 1
55+
return counts
56+
57+
# -------------------------
58+
# Example: 3 qubits in |5⟩
59+
# -------------------------
60+
n_qubits = 3
61+
N = 2 ** n_qubits
62+
state = np.zeros(N, dtype=complex)
63+
state[5] = 1.0 # Start in |5⟩ = |101⟩
64+
65+
# Apply QFT
66+
qft_state = qft_step_by_step(state.copy(), n_qubits)
67+
68+
# Apply inverse QFT to check if we recover original
69+
inv_qft_state = qft_step_by_step(qft_state.copy(), n_qubits, inverse=True)
70+
71+
# Measurement sampling from QFT output
72+
measurement_results = measure_state(qft_state, shots=1024)
73+
74+
# -------------------------
75+
# Output
76+
# -------------------------
77+
print("QFT amplitudes:")
78+
for i, amp in enumerate(qft_state):
79+
print(f"|{i:03b}>: {amp:.4f}")
80+
81+
print("\nInverse QFT applied back (should recover |5⟩):")
82+
for i, amp in enumerate(inv_qft_state):
83+
print(f"|{i:03b}>: {amp:.4f}")
84+
85+
print("\nMeasurement results from QFT output:")
86+
for bitstring, count in sorted(measurement_results.items()):
87+
print(f"{bitstring}: {count}")
88+
89+
"""
90+
Key Features
91+
Inverse QFT (via negative controlled-phase angles and reversed order),
92+
Measurement sampling (random draws from the final state’s probability distribution).
93+
94+
Forward and inverse QFT logic built in.
95+
Uses exact Hadamard and controlled phase gates.
96+
Simulates quantum measurement from the QFT result.
97+
Bit-reversal swaps ensure correct output basis alignment.
98+
"""
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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+
"""Initialize the quantum state to |index⟩"""
11+
self.state = np.zeros(self.N, dtype=complex)
12+
self.state[index] = 1.0
13+
14+
def hadamard(self):
15+
return np.array([[1, 1], [1, -1]]) / np.sqrt(2)
16+
17+
def apply_single_qubit_gate(self, gate, target):
18+
"""Apply a single-qubit gate to the target qubit."""
19+
I = np.eye(2)
20+
ops = [I] * self.n_qubits
21+
ops[target] = gate
22+
U = ops[0]
23+
for op in ops[1:]:
24+
U = np.kron(U, op)
25+
self.state = U @ self.state
26+
27+
def apply_controlled_phase(self, control, target, theta):
28+
"""Apply a controlled phase rotation gate."""
29+
size = self.N
30+
U = np.eye(size, dtype=complex)
31+
for i in range(size):
32+
b = format(i, f'0{self.n_qubits}b')
33+
if b[self.n_qubits - 1 - control] == '1' and b[self.n_qubits - 1 - target] == '1':
34+
U[i, i] *= np.exp(1j * theta)
35+
self.state = U @ self.state
36+
37+
def swap_registers(self):
38+
"""Swap qubit order (bit-reversal permutation)."""
39+
perm = [int(format(i, f'0{self.n_qubits}b')[::-1], 2) for i in range(self.N)]
40+
self.state = self.state[perm]
41+
42+
def apply_qft(self, inverse=False):
43+
"""Apply (inverse) QFT to the current state."""
44+
for target in range(self.n_qubits):
45+
idx = self.n_qubits - 1 - target
46+
for ctrl_offset in range(1, self.n_qubits - target):
47+
control = self.n_qubits - 1 - (target + ctrl_offset)
48+
angle = np.pi / (2 ** ctrl_offset)
49+
if inverse:
50+
angle *= -1
51+
self.apply_controlled_phase(control, idx, angle)
52+
self.apply_single_qubit_gate(self.hadamard(), idx)
53+
self.swap_registers()
54+
def measure(self, shots=1024):
55+
"""Simulate measurement outcomes."""
56+
probs = np.abs(self.state) ** 2
57+
outcomes = np.random.choice(self.N, size=shots, p=probs)
58+
counts = {}
59+
for o in outcomes:
60+
bitstring = format(o, f'0{self.n_qubits}b')
61+
counts[bitstring] = counts.get(bitstring, 0) + 1
62+
return counts
63+
64+
def print_amplitudes(self, title="Quantum State"):
65+
print(f"\n{title}:")
66+
for i, amp in enumerate(self.state):
67+
print(f"|{i:0{self.n_qubits}b}>: {amp:.4f}")
68+
69+
# ---------------------------
70+
# Example usage
71+
# ---------------------------
72+
if __name__ == "__main__":
73+
qft = QuantumFourierTransform(n_qubits=3)
74+
qft.initialize_basis_state(5) # Set initial state to |5⟩
75+
76+
qft.print_amplitudes("Initial State")
77+
78+
qft.apply_qft() # Apply QFT
79+
qft.print_amplitudes("After QFT")
80+
81+
samples = qft.measure(shots=1024)
82+
print("\nMeasurement results:")
83+
for k, v in sorted(samples.items()):
84+
print(f"{k}: {v}")
85+
86+
qft.apply_qft(inverse=True) # Apply inverse QFT to recover original
87+
qft.print_amplitudes("After Inverse QFT (should be |5⟩)")
88+
89+
90+
91+
"""
92+
reusable Python class called QuantumFourierTransform that encapsulates everything:
93+
94+
95+
96+
Initialization of state
97+
QFT and inverse QFT
98+
Measurement sampling
99+
Easy customization of qubit number and input state
100+
101+
Benefits of This Class Design
102+
Reusable and clean.
103+
Makes it easy to switch between QFT and inverse QFT.
104+
Includes state visualization and measurement simulation.
105+
Fully self-contained — just run the script as-is.
106+
"""
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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

Comments
 (0)