-
Notifications
You must be signed in to change notification settings - Fork 66
/
QuSim.py
133 lines (115 loc) · 3.96 KB
/
QuSim.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
from functools import reduce
import numpy as np
class gates:
i = np.complex(0, 1)
singleQubitGates = {
# Pauli-X / Not Gate
'X': np.matrix([
[0, 1],
[1, 0]
]),
# Pauli-Y Gate
'Y': np.matrix([
[0, -i],
[i, 0]
]),
# Pauli-Z Gate
'Z': np.matrix([
[1, 0],
[0, -1]
]),
# Hadamard Gate
'H': np.multiply(1. / np.sqrt(2), np.matrix([
[1, 1],
[1, -1]
])),
# Identity Gate
'Id': np.eye(2),
# S & S Dagger Gate
'S': np.matrix([
[1, 0],
[0, i]
]),
'SDagger': np.matrix([
[1, 0],
[0, i]
]).conjugate().transpose(),
# T & T Dagger / Pi over 8 Gate
'T': np.matrix([
[1, 0],
[0, np.e**(i * np.pi / 4.)]
]),
'TDagger': np.matrix([
[1, 0],
[0, np.e**(i * np.pi / 4.)]
]).conjugate().transpose()
}
@staticmethod
def generateGate(gate, numQubits, qubit1, qubit2=1):
if (gate == 'CNOT'):
control = qubit1
target = qubit2
identity = np.eye(2)
X = gates.singleQubitGates['X']
# NaN is our 'C' from the multi qubit gate generation formula
C = np.mat([
[float('nan'), 0],
[0, 1]
])
# Set the gate order
gateOrder = []
for i in range(1, numQubits + 1):
if (i == control):
gateOrder.append(C)
elif (i == target):
gateOrder.append(X)
else:
gateOrder.append(identity)
# Generate the gate and then replace the NaNs to Id gates
newGate = reduce(np.kron, gateOrder)
n = newGate.shape[0]
return np.mat([[newGate[i, j] if not np.isnan(newGate[i, j]) else 1 if i == j else 0 for j in range(n)] for i in range(n)])
else:
# Put these here for handyness
identity = gates.singleQubitGates['Id']
mainGate = gates.singleQubitGates[gate]
gateOrder = (mainGate if i == qubit1 else identity
for i in range(1, numQubits + 1))
return reduce(np.kron, gateOrder)
class QuantumRegister:
def __init__(self, numQubits):
self.numQubits = numQubits
# The number of amplitudes needed is 2^n, where N is the
# number of qubits, So start with a vector of zeros.
self.amplitudes = np.zeros(2**numQubits)
# Set the probabilit of getting 0 when measured to 1
self.amplitudes[0] = 1
self.value = False
def applyGate(self, gate, qubit1, qubit2=-1):
if self.value:
raise ValueError('Cannot Apply Gate to Measured Register')
else:
# Generate the gate matrix
gateMatrix = gates.generateGate(
gate, self.numQubits, qubit1, qubit2)
# Calculate the new state vector by multiplying by the gate
self.amplitudes = np.dot(self.amplitudes, gateMatrix)
def measure(self):
if self.value:
return self.value
else:
# Get this list of probabilities, by squaring the absolute
# value of the amplitudes
self.probabilities = []
for amp in np.nditer(self.amplitudes):
probability = np.absolute(amp)**2
self.probabilities.append(probability)
# Now, we need to make a weighted random choice of all of the possible
# output states (done with the range function)
results = list(range(len(self.probabilities)))
self.value = np.binary_repr(
np.random.choice(results, p=self.probabilities),
self.numQubits
)
return self.value
# And thats it!