@@ -1102,6 +1102,124 @@ algorithms and demonstrates the power of quantum Fourier transforms in
11021102computational tasks. For many-body simulations it is however not as efficient as the VQE algorithm.
11031103
11041104
1105+ !split
1106+ ===== QPE code without the usage of explicit quantum libraries =====
1107+
1108+ !bc pycod
1109+ import numpy as np
1110+ import matplotlib.pyplot as plt
1111+ from matplotlib.animation import FuncAnimation
1112+ from matplotlib import cm
1113+
1114+ def unitary_operator():
1115+ # Controlled Phase Gate (CP) with a phase of pi/4
1116+ return np.array([[1, 0, 0, 0],
1117+ [0, 1, 0, 0],
1118+ [0, 0, np.exp(1j * np.pi / 4), 0],
1119+ [0, 0, 0, np.exp(1j * np.pi / 4)]], dtype=complex)
1120+
1121+ def eigenstate():
1122+ # Superposition state |ψ⟩ = (|0⟩ + |1⟩)/√2
1123+ return np.array([1/np.sqrt(2), 1/np.sqrt(2)], dtype=complex)
1124+
1125+ class QuantumPhaseEstimation:
1126+ def __init__(self, n_qubits, unitary, eigenstate, inverse=False):
1127+ self.n_qubits = n_qubits
1128+ self.N = 2 ** n_qubits
1129+ self.unitary = unitary # Unitary operator U
1130+ self.eigenstate = eigenstate # Eigenstate |ψ⟩
1131+ self.inverse = inverse # Whether to reverse the QFT
1132+ self.state = np.zeros(self.N, dtype=complex)
1133+ self.state[0] = 1.0 # Initialize ancillary qubits to |0...0⟩
1134+
1135+ def apply_single_qubit_gate(self, gate, target):
1136+ I = np.eye(2)
1137+ ops = [I] * self.n_qubits
1138+ ops[target] = gate
1139+ U = ops[0]
1140+ for op in ops[1:]:
1141+ U = np.kron(U, op)
1142+ self.state = U @ self.state
1143+
1144+ def hadamard(self):
1145+ return np.array([[1, 1], [1, -1]]) / np.sqrt(2)
1146+
1147+ def apply_controlled_unitary(self, control, target, exp_power):
1148+ """Apply controlled unitary operation U^(2^j)."""
1149+ U = np.eye(self.N, dtype=complex)
1150+ for i in range(self.N):
1151+ b = format(i, f'0{self.n_qubits}b')
1152+ if b[self.n_qubits - 1 - control] == '1':
1153+ U[i, i] = np.exp(1j * (np.pi / (2 ** exp_power)))
1154+ self.state = U @ self.state
1155+
1156+ def apply_qft(self, inverse=False):
1157+ """Apply Quantum Fourier Transform on ancillary qubits."""
1158+ for target in range(self.n_qubits):
1159+ idx = self.n_qubits - 1 - target
1160+ for offset in range(1, self.n_qubits - target):
1161+ control = self.n_qubits - 1 - (target + offset)
1162+ angle = np.pi / (2 ** offset)
1163+ if inverse:
1164+ angle *= -1
1165+ self.apply_controlled_unitary(control, idx, angle)
1166+ self.apply_single_qubit_gate(self.hadamard(), idx)
1167+
1168+ self.swap_registers()
1169+
1170+ def swap_registers(self):
1171+ perm = [int(format(i, f'0{self.n_qubits}b')[::-1], 2) for i in range(self.N)]
1172+ self.state = self.state[perm]
1173+
1174+ def measure(self):
1175+ """Measure the ancillary qubits."""
1176+ probs = np.abs(self.state) ** 2
1177+ outcomes = np.random.choice(self.N, size=1024, p=probs)
1178+ counts = {}
1179+ for o in outcomes:
1180+ bitstring = format(o, f'0{self.n_qubits}b')
1181+ counts[bitstring] = counts.get(bitstring, 0) + 1
1182+ return counts
1183+
1184+ def plot_probability_distribution(self, results):
1185+ bitstrings = sorted(results.keys())
1186+ counts = [results[b] for b in bitstrings]
1187+ plt.bar(bitstrings, counts)
1188+ plt.xlabel("Bitstring")
1189+ plt.ylabel("Counts")
1190+ plt.title("QPE Measurement Results")
1191+ plt.xticks(rotation=45)
1192+ plt.tight_layout()
1193+ plt.show()
1194+
1195+ def apply_phase_estimation(self):
1196+ """Perform Quantum Phase Estimation (QPE)."""
1197+ # Apply Hadamard to all ancillary qubits
1198+ for target in range(self.n_qubits):
1199+ self.apply_single_qubit_gate(self.hadamard(), target)
1200+
1201+ # Apply controlled unitaries (U^(2^j))
1202+ for j in range(self.n_qubits):
1203+ self.apply_controlled_unitary(j, self.n_qubits, j)
1204+
1205+ # Apply QFT
1206+ self.apply_qft(inverse=True)
1207+
1208+ # Measure the ancillary qubits
1209+ results = self.measure()
1210+ self.plot_probability_distribution(results)
1211+
1212+ # ---------------------------
1213+ # Example usage
1214+ # ---------------------------
1215+ if __name__ == "__main__":
1216+ n_qubits = 3 # Number of ancillary qubits
1217+ qpe = QuantumPhaseEstimation(n_qubits, unitary_operator(), eigenstate())
1218+ qpe.apply_phase_estimation()
1219+
1220+ !ec
1221+
1222+
11051223
11061224!split
11071225===== Brief overview of Shor's algorithm =====
@@ -1238,238 +1356,3 @@ o Highlights the potential of quantum computing to break RSA.
12381356
12391357
12401358
1241- !split
1242- ===== QPE code without the usage of explicit quantum libraries =====
1243-
1244- !bc pycod
1245- import numpy as np
1246- import matplotlib.pyplot as plt
1247- from matplotlib.animation import FuncAnimation
1248- from matplotlib import cm
1249-
1250- class QuantumPhaseEstimation:
1251- def __init__(self, n_qubits, unitary, eigenstate, inverse=False):
1252- self.n_qubits = n_qubits
1253- self.N = 2 ** n_qubits
1254- self.unitary = unitary # Unitary operator U
1255- self.eigenstate = eigenstate # Eigenstate |ψ⟩
1256- self.inverse = inverse # Whether to reverse the QFT
1257- self.state = np.zeros(self.N, dtype=complex)
1258- self.state[0] = 1.0 # Initialize ancillary qubits to |0...0⟩
1259-
1260- def apply_single_qubit_gate(self, gate, target):
1261- I = np.eye(2)
1262- ops = [I] * self.n_qubits
1263- ops[target] = gate
1264- U = ops[0]
1265- for op in ops[1:]:
1266- U = np.kron(U, op)
1267- self.state = U @ self.state
1268-
1269- def hadamard(self):
1270- return np.array([[1, 1], [1, -1]]) / np.sqrt(2)
1271-
1272- def apply_controlled_unitary(self, control, target, exp_power):
1273- """Apply controlled unitary operation U^(2^j)."""
1274- U = np.eye(self.N, dtype=complex)
1275- for i in range(self.N):
1276- b = format(i, f'0{self.n_qubits}b')
1277- if b[self.n_qubits - 1 - control] == '1':
1278- U[i, i] = np.exp(1j * (np.pi / (2 ** exp_power)))
1279- self.state = U @ self.state
1280-
1281- def apply_qft(self, inverse=False):
1282- """Apply Quantum Fourier Transform on ancillary qubits."""
1283- for target in range(self.n_qubits):
1284- idx = self.n_qubits - 1 - target
1285- for offset in range(1, self.n_qubits - target):
1286- control = self.n_qubits - 1 - (target + offset)
1287- angle = np.pi / (2 ** offset)
1288- if inverse:
1289- angle *= -1
1290- self.apply_controlled_unitary(control, idx, angle)
1291- self.apply_single_qubit_gate(self.hadamard(), idx)
1292-
1293- self.swap_registers()
1294-
1295- def swap_registers(self):
1296- perm = [int(format(i, f'0{self.n_qubits}b')[::-1], 2) for i in range(self.N)]
1297- self.state = self.state[perm]
1298-
1299- def measure(self):
1300- """Measure the ancillary qubits."""
1301- probs = np.abs(self.state) ** 2
1302- outcomes = np.random.choice(self.N, size=1024, p=probs)
1303- counts = {}
1304- for o in outcomes:
1305- bitstring = format(o, f'0{self.n_qubits}b')
1306- counts[bitstring] = counts.get(bitstring, 0) + 1
1307- return counts
1308-
1309- def plot_probability_distribution(self, results):
1310- bitstrings = sorted(results.keys())
1311- counts = [results[b] for b in bitstrings]
1312- plt.bar(bitstrings, counts)
1313- plt.xlabel("Bitstring")
1314- plt.ylabel("Counts")
1315- plt.title("QPE Measurement Results")
1316- plt.xticks(rotation=45)
1317- plt.tight_layout()
1318- plt.show()
1319-
1320- def apply_phase_estimation(self):
1321- """Perform Quantum Phase Estimation (QPE)."""
1322- # Apply Hadamard to all ancillary qubits
1323- for target in range(self.n_qubits):
1324- self.apply_single_qubit_gate(self.hadamard(), target)
1325-
1326- # Apply controlled unitaries (U^(2^j))
1327- for j in range(self.n_qubits):
1328- self.apply_controlled_unitary(j, self.n_qubits, j)
1329-
1330- # Apply QFT
1331- self.apply_qft(inverse=True)
1332-
1333- # Measure the ancillary qubits
1334- results = self.measure()
1335- self.plot_probability_distribution(results)
1336-
1337- # Example Unit and Eigenstate
1338- def unitary_operator():
1339- # Simple controlled-phase gate (for example)
1340- return np.array([[1, 0], [0, np.exp(1j * np.pi / 4)]], dtype=complex)
1341-
1342- def eigenstate():
1343- return np.array([1, 0], dtype=complex) # eigenstate |1⟩
1344-
1345- # ---------------------------
1346- # Example usage
1347- # ---------------------------
1348- if __name__ == "__main__":
1349- n_qubits = 3
1350- qpe = QuantumPhaseEstimation(n_qubits, unitary_operator(), eigenstate())
1351- qpe.apply_phase_estimation()
1352-
1353- !ec
1354-
1355-
1356- !split
1357- ===== QPE code without the usage of explicit quantum libraries =====
1358-
1359- !bc pycod
1360- import numpy as np
1361- import matplotlib.pyplot as plt
1362- from matplotlib.animation import FuncAnimation
1363- from matplotlib import cm
1364-
1365- def unitary_operator():
1366- # Controlled Phase Gate (CP) with a phase of pi/4
1367- return np.array([[1, 0, 0, 0],
1368- [0, 1, 0, 0],
1369- [0, 0, np.exp(1j * np.pi / 4), 0],
1370- [0, 0, 0, np.exp(1j * np.pi / 4)]], dtype=complex)
1371-
1372- def eigenstate():
1373- # Superposition state |ψ⟩ = (|0⟩ + |1⟩)/√2
1374- return np.array([1/np.sqrt(2), 1/np.sqrt(2)], dtype=complex)
1375-
1376- class QuantumPhaseEstimation:
1377- def __init__(self, n_qubits, unitary, eigenstate, inverse=False):
1378- self.n_qubits = n_qubits
1379- self.N = 2 ** n_qubits
1380- self.unitary = unitary # Unitary operator U
1381- self.eigenstate = eigenstate # Eigenstate |ψ⟩
1382- self.inverse = inverse # Whether to reverse the QFT
1383- self.state = np.zeros(self.N, dtype=complex)
1384- self.state[0] = 1.0 # Initialize ancillary qubits to |0...0⟩
1385-
1386- def apply_single_qubit_gate(self, gate, target):
1387- I = np.eye(2)
1388- ops = [I] * self.n_qubits
1389- ops[target] = gate
1390- U = ops[0]
1391- for op in ops[1:]:
1392- U = np.kron(U, op)
1393- self.state = U @ self.state
1394-
1395- def hadamard(self):
1396- return np.array([[1, 1], [1, -1]]) / np.sqrt(2)
1397-
1398- def apply_controlled_unitary(self, control, target, exp_power):
1399- """Apply controlled unitary operation U^(2^j)."""
1400- U = np.eye(self.N, dtype=complex)
1401- for i in range(self.N):
1402- b = format(i, f'0{self.n_qubits}b')
1403- if b[self.n_qubits - 1 - control] == '1':
1404- U[i, i] = np.exp(1j * (np.pi / (2 ** exp_power)))
1405- self.state = U @ self.state
1406-
1407- def apply_qft(self, inverse=False):
1408- """Apply Quantum Fourier Transform on ancillary qubits."""
1409- for target in range(self.n_qubits):
1410- idx = self.n_qubits - 1 - target
1411- for offset in range(1, self.n_qubits - target):
1412- control = self.n_qubits - 1 - (target + offset)
1413- angle = np.pi / (2 ** offset)
1414- if inverse:
1415- angle *= -1
1416- self.apply_controlled_unitary(control, idx, angle)
1417- self.apply_single_qubit_gate(self.hadamard(), idx)
1418-
1419- self.swap_registers()
1420-
1421- def swap_registers(self):
1422- perm = [int(format(i, f'0{self.n_qubits}b')[::-1], 2) for i in range(self.N)]
1423- self.state = self.state[perm]
1424-
1425- def measure(self):
1426- """Measure the ancillary qubits."""
1427- probs = np.abs(self.state) ** 2
1428- outcomes = np.random.choice(self.N, size=1024, p=probs)
1429- counts = {}
1430- for o in outcomes:
1431- bitstring = format(o, f'0{self.n_qubits}b')
1432- counts[bitstring] = counts.get(bitstring, 0) + 1
1433- return counts
1434-
1435- def plot_probability_distribution(self, results):
1436- bitstrings = sorted(results.keys())
1437- counts = [results[b] for b in bitstrings]
1438- plt.bar(bitstrings, counts)
1439- plt.xlabel("Bitstring")
1440- plt.ylabel("Counts")
1441- plt.title("QPE Measurement Results")
1442- plt.xticks(rotation=45)
1443- plt.tight_layout()
1444- plt.show()
1445-
1446- def apply_phase_estimation(self):
1447- """Perform Quantum Phase Estimation (QPE)."""
1448- # Apply Hadamard to all ancillary qubits
1449- for target in range(self.n_qubits):
1450- self.apply_single_qubit_gate(self.hadamard(), target)
1451-
1452- # Apply controlled unitaries (U^(2^j))
1453- for j in range(self.n_qubits):
1454- self.apply_controlled_unitary(j, self.n_qubits, j)
1455-
1456- # Apply QFT
1457- self.apply_qft(inverse=True)
1458-
1459- # Measure the ancillary qubits
1460- results = self.measure()
1461- self.plot_probability_distribution(results)
1462-
1463- # ---------------------------
1464- # Example usage
1465- # ---------------------------
1466- if __name__ == "__main__":
1467- n_qubits = 3 # Number of ancillary qubits
1468- qpe = QuantumPhaseEstimation(n_qubits, unitary_operator(), eigenstate())
1469- qpe.apply_phase_estimation()
1470-
1471- !ec
1472-
1473-
1474-
1475-
0 commit comments