Skip to content

Commit 9cff2d4

Browse files
committed
update
1 parent cf22c17 commit 9cff2d4

File tree

8 files changed

+3103
-1679
lines changed

8 files changed

+3103
-1679
lines changed

doc/pub/week12/html/week12-bs.html

Lines changed: 138 additions & 276 deletions
Large diffs are not rendered by default.

doc/pub/week12/html/week12-reveal.html

Lines changed: 136 additions & 269 deletions
Large diffs are not rendered by default.

doc/pub/week12/html/week12-solarized.html

Lines changed: 137 additions & 274 deletions
Large diffs are not rendered by default.

doc/pub/week12/html/week12.html

Lines changed: 137 additions & 274 deletions
Large diffs are not rendered by default.
0 Bytes
Binary file not shown.

doc/pub/week12/ipynb/week12.ipynb

Lines changed: 222 additions & 351 deletions
Large diffs are not rendered by default.

doc/src/week12/.ipynb_checkpoints/week12-checkpoint.ipynb

Lines changed: 2215 additions & 0 deletions
Large diffs are not rendered by default.

doc/src/week12/week12.do.txt

Lines changed: 118 additions & 235 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,124 @@ algorithms and demonstrates the power of quantum Fourier transforms in
11021102
computational 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

Comments
 (0)