├── LICENSE ├── PythonImplementation ├── QuantumSimulator │ ├── gates.py │ └── register.py ├── README.md ├── example0.py ├── example1.py └── example2.py ├── README.md ├── explanation └── math.pdf └── src ├── QuantumComputerSimulator.h ├── methods.cpp ├── methods.h ├── qalgorithms.cpp ├── qalgorithms.h ├── quantum.cpp ├── quantum.h ├── rand.cpp ├── rand.h ├── test.cpp ├── types.h ├── unitary.cpp └── unitary.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Joseph Iosue 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PythonImplementation/QuantumSimulator/gates.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | exp, PI, cos, sin = np.exp, np.pi, np.cos, np.sin 4 | 5 | sigma_x = [[0, 1], [1, 0]] 6 | sigma_y = [[0, -1j], [1j, 0]] 7 | sigma_z = [[1, 0], [0, -1]] 8 | 9 | 10 | class Gate: 11 | 12 | def __init__(self, unitary, qubits): 13 | """ 14 | unitary is list of list representing unitary matrix 15 | qubits is tuple in order of qubits that unitary acts on 16 | """ 17 | self.unitary, self.qubits = np.array(unitary), qubits 18 | self.dimension, self.num_qubits = len(unitary), len(qubits) 19 | 20 | def __getitem__(self, item): 21 | """ Gate[i][j] gets the (i, j) element of the unitary matrix """ 22 | return self.unitary[item] 23 | 24 | def __call__(self, register): 25 | """ 26 | Apply gate to register. 27 | 28 | :param register: Register object to apply the gate to 29 | :return: the register, so that we can string gate function calls. 30 | ie, gate1(gate2(gate3(register))) 31 | """ 32 | register.apply_gate(self) 33 | return register 34 | 35 | def full_unitary(self, num_qubits): 36 | """ 37 | Find the full unitary matrix of the gate on the full Hilbert space of 38 | dimension 2^num_qubits. ONLY WORKS FOR SINGLE QUBIT GATES RIGHT NOW 39 | """ 40 | unitary = np.kron(np.eye(1 << self.qubits[0]), self.unitary) 41 | unitary = np.kron(unitary, np.eye(1 << (num_qubits-self.qubits[0]-1))) 42 | return unitary 43 | 44 | def __pow__(self, power): 45 | return Gate([list(x) for x in np.array(self.unitary)**power], 46 | self.qubits) 47 | 48 | # def __mul__(self, other): 49 | # """ self * other """ 50 | 51 | 52 | 53 | 54 | class H(Gate): 55 | c = 1.0/2.0**0.5 + 0.0j 56 | unitary = np.array([ 57 | [c, c], 58 | [c, -c] 59 | ]) 60 | def __init__(self, qubit): 61 | super().__init__(H.unitary, (qubit,)) 62 | 63 | def __str__(self): 64 | return "H(%d)" % self.qubits[0] 65 | 66 | class CX(Gate): 67 | unitary = np.array([ 68 | [1.0, 0.0, 0.0, 0.0], 69 | [0.0, 1.0, 0.0, 0.0], 70 | [0.0, 0.0, 0.0, 1.0], 71 | [0.0, 0.0, 1.0, 0.0] 72 | ]) 73 | def __init__(self, control_qubit, target_qubit): 74 | # qubits should be tuple (control, target) 75 | super().__init__(CX.unitary, (control_qubit, target_qubit)) 76 | 77 | def __str__(self): 78 | return "CX" + str(self.qubits) 79 | 80 | class X(Gate): 81 | unitary = np.array(sigma_x) 82 | def __init__(self, qubit): 83 | super().__init__(X.unitary, (qubit,)) 84 | 85 | def __str__(self): 86 | return "X%d" % self.qubits[0] 87 | 88 | class Y(Gate): 89 | unitary = np.array(sigma_y) 90 | def __init__(self, qubit): 91 | super().__init__(Y.unitary, (qubit,)) 92 | 93 | def __str__(self): 94 | return "Y%d" % self.qubits[0] 95 | 96 | class Z(Gate): 97 | unitary = np.array(sigma_z) 98 | def __init__(self, qubit): 99 | super().__init__(Z.unitary, (qubit,)) 100 | 101 | def __str__(self): 102 | return "Z%d" % self.qubits[0] 103 | 104 | class T(Gate): 105 | unitary = [[0.0]*8 for _ in range(8)] 106 | for i in range(6): unitary[i][i] = 1.0 107 | unitary[6][7] = 1.0 108 | unitary[7][6] = 1.0 109 | unitary = np.array(unitary) 110 | def __init__(self, *qubits): 111 | """ qubits should be a tuple of length 3 """ 112 | super().__init__(T.unitary, qubits) 113 | 114 | def __str__(self): 115 | return "T" + str(self.qubits) 116 | 117 | class S(Gate): 118 | unitary = np.array([ 119 | [1.0, 0.0, 0.0, 0.0], 120 | [0.0, 0.0, 1.0, 0.0], 121 | [0.0, 1.0, 0.0, 0.0], 122 | [0.0, 0.0, 0.0, 1.0] 123 | ]) 124 | def __init__(self, *qubits): 125 | """ swap two qubits. qubits should be tuple of length 2 """ 126 | super().__init__(S.unitary, qubits) 127 | 128 | def __str__(self): 129 | return "S" + str(self.qubits) 130 | 131 | class P(Gate): 132 | """ Phase shift P = |0><0| + exp(i theta) |1><1| """ 133 | unitary = lambda angle: np.array([ 134 | [1.0, 0.0], 135 | [0.0, exp(1.0j*angle)] 136 | ]) 137 | def __init__(self, angle, qubit): 138 | super().__init__(P.unitary(angle), (qubit,)) 139 | self.angle = angle 140 | 141 | def __str__(self): 142 | return "P" + str((self.angle,) + self.qubits) 143 | 144 | class CP(Gate): 145 | unitary = lambda angle: np.array([ 146 | [1.0, 0.0, 0.0, 0.0], 147 | [0.0, 1.0, 0.0, 0.0], 148 | [0.0, 0.0, 1.0, 0.0], 149 | [0.0, 0.0, 0.0, exp(1.0j*angle)] 150 | ]) 151 | def __init__(self, angle, control_qubit, target_qubit): 152 | qubits = (control_qubit, target_qubit) 153 | super().__init__(CP.unitary(angle), qubits) 154 | self.angle = angle 155 | 156 | def __str__(self): 157 | return "CP" + str((self.angle,) + self.qubits) 158 | 159 | class RX(Gate): 160 | unitary = lambda angle: np.array([ 161 | [cos(angle/2), -1.0j*sin(angle/2)], 162 | [-1.0j*sin(angle/2), cos(angle/2)] 163 | ]) 164 | def __init__(self, angle, qubit): 165 | """ rotate the qubit around the x axis by an angle """ 166 | super().__init__(RX.unitary(angle), (qubit,)) 167 | self.angle = angle 168 | 169 | def __str__(self): 170 | return "RX" + str((self.angle,) + self.qubits) 171 | 172 | class RY(Gate): 173 | unitary = lambda angle: np.array([ 174 | [cos(angle/2), -sin(angle/2)], 175 | [sin(angle/2), cos(angle/2)] 176 | ]) 177 | def __init__(self, angle, qubit): 178 | """ rotate the qubit around the y axis by an angle """ 179 | super().__init__(RY.unitary(angle), (qubit,)) 180 | self.angle = angle 181 | 182 | def __str__(self): 183 | return "RY" + str((self.angle,) + self.qubits) 184 | 185 | class RZ(Gate): 186 | unitary = lambda angle: np.array([ 187 | [exp(-1.0j*angle/2), 0.0], 188 | [0.0, exp(1.0j*angle/2)] 189 | ]) 190 | def __init__(self, angle, qubit): 191 | """ rotate the qubit around the z axis by an angle """ 192 | super().__init__(RZ.unitary(angle), (qubit,)) 193 | self.angle = angle 194 | 195 | def __str__(self): 196 | return "RZ" + str((self.angle,) + self.qubits) 197 | 198 | class U3(Gate): 199 | """ u3(th, phi, lam) = Rz(phi)Ry(th)Rz(lam), see arxiv:1707.03429 """ 200 | unitary = lambda theta, phi, lam: np.array([ 201 | [exp(-1j*(phi+lam)/2)*cos(theta/2), 202 | -exp(-1j*(phi-lam)/2)*sin(theta/2)], 203 | [exp(1j*(phi-lam)/2)*sin(theta/2), 204 | exp(1j*(phi+lam)/2)*cos(theta/2)] 205 | ]) 206 | def __init__(self, theta, phi, lam, qubit): 207 | super().__init__(U3.unitary(theta, phi, lam), (qubit,)) 208 | self.params = theta, phi, lam 209 | 210 | def __str__(self): 211 | return "U3" + str(self.params + self.qubits) 212 | 213 | 214 | def string_to_gate(string): 215 | return eval(string.upper()) 216 | 217 | 218 | def apply_gate(string, register): 219 | """ apply the gate represented by string to the register """ 220 | string_to_gate(string)(register) 221 | 222 | 223 | def apply_algorithm(algorithm, register): 224 | for gate in algorithm: apply_gate(gate, register) 225 | -------------------------------------------------------------------------------- /PythonImplementation/QuantumSimulator/register.py: -------------------------------------------------------------------------------- 1 | import random 2 | import numpy as np 3 | import QuantumSimulator.gates as gates 4 | 5 | 6 | def all_states(num_qubits): 7 | """ 8 | Generator, yields '00', '01', '10', '11' 9 | for two qubits, and similar for more 10 | """ 11 | if num_qubits == 1: yield from ("0", "1") 12 | elif num_qubits > 1: 13 | for s in all_states(num_qubits-1): 14 | yield from (s+"0", s+"1") 15 | 16 | 17 | class Register(dict): 18 | 19 | def __init__(self, num_qubits): 20 | """ initialize register to |"0"*num_qubits> """ 21 | super().__init__() 22 | self.num_qubits = num_qubits 23 | self["0"*num_qubits] = 1.0+0.0j 24 | 25 | def __getitem__(self, item): 26 | """ 27 | return the amplitude of the state. setitem still works 28 | by inheriting from dict class 29 | """ 30 | return super().__getitem__(item) if item in self else 0.0+0.0j 31 | 32 | def amplitude(self, state): 33 | return self[state] 34 | 35 | def probability(self, state): 36 | return abs(self[state])**2 37 | 38 | def apply_gate(self, gate): 39 | """ apply Gate object to the register """ 40 | assert gate.dimension == 1 << gate.num_qubits, ( # 1 << a = 2**a 41 | "Unitary dimension is not correct to be applied to the input qubits" 42 | ) 43 | 44 | old, temp_states = self.copy(), [x for x in all_states(gate.num_qubits)] 45 | for state in old: 46 | s = "" 47 | for q in gate.qubits: s += state[q] 48 | r = int(s, base=2) 49 | self[state] -= (1.0 - gate[r][r]) * old[state] 50 | if self.probability(state) < 1e-16: self.pop(state) # zero beyond machine precision 51 | 52 | j = 0 53 | for k in temp_states: 54 | if j != r: 55 | s = list(state) 56 | for l in range(len(k)): s[gate.qubits[l]] = k[l] 57 | s = "".join(s) 58 | c = gate[j][r] * old[state] 59 | if s in self: 60 | self[s] += c 61 | if self.probability(s) < 1e-16: self.pop(s) 62 | elif c != 0.0: self[s] = c 63 | j += 1 64 | 65 | def measure(self): 66 | """ Measure the system and collapse it into a state """ 67 | r = random.random() 68 | total = 0.0 69 | for state in self: 70 | total += self.probability(state) 71 | if r <= total: # collapse the state 72 | self.clear() 73 | self[state] = 1.0+0.0j 74 | return state 75 | 76 | def measure_qubit(self, qubit): 77 | """ measure the qubit and collapse the system """ 78 | zero_prob = 0.0 79 | for state in self: 80 | if state[qubit] == "0": zero_prob += self.probability(state) 81 | 82 | if random.random() < zero_prob: v, p = "0", zero_prob**0.5 83 | else: v, p = "1", (1-zero_prob)**0.5 84 | 85 | old_register = self.copy() 86 | self.clear() 87 | 88 | for (state, amp) in old_register.items(): 89 | if state[qubit] == v: self[state] = amp / p 90 | 91 | return v 92 | 93 | def duplicate(self): 94 | """ return a copy of the register """ 95 | reg = Register(self.num_qubits) 96 | for (key, item) in self.items(): reg[key] = item 97 | return reg 98 | 99 | def ket(self): 100 | """ 101 | Returns the ket vector of the state in the computational basis. 102 | Returns in the form of a numpy array. 103 | """ 104 | ket = np.zeros((1 << self.num_qubits, 1))*0j 105 | for state in self: ket[int(state, base=2)][0] = self[state] 106 | return ket 107 | 108 | def bra(self): 109 | """ 110 | Returns the bra vector of the state in the computational basis. 111 | Returns in the form of a numpy array. 112 | """ 113 | return np.conjugate(np.transpose(self.ket())) 114 | 115 | def density_matrix(self): 116 | """ 117 | Returns the density matrix representing the state of ther resgister. 118 | Returns in the form of a numpy array. Note that the Register class 119 | only supports pure states, so the density matrix will be that of a 120 | pure state 121 | """ 122 | ket = self.ket() 123 | return ket @ np.conjugate(np.transpose(ket)) 124 | 125 | def apply_algorithm(self, algorithm): 126 | gates.apply_algorithm(algorithm, self) 127 | 128 | 129 | 130 | class MixedRegister: 131 | 132 | def __init__(self, registers): 133 | """ 134 | registers is a list of tuples of the form 135 | registers = [(.2, reg1), (.8, reg2)] 136 | where the 0th index of the tuple corresponds to the statistical prob 137 | of being in the pure state, where the 1st index of the tuple is that 138 | pure state (a Register object). 139 | """ 140 | self.registers = registers 141 | self.num_qubits = registers[0][1].num_qubits 142 | assert(all(r[1].num_qubits == self.num_qubits for r in registers)) 143 | 144 | def apply_gate(self, gate): 145 | for _, reg in self.registers: reg.apply_gate(gate) 146 | 147 | def density_matrix(self): 148 | return sum(p*reg.density_matrix() for p, reg in self.registers) 149 | 150 | def coefficient(self, state1, state2): 151 | """ Return the coefficient in front of |state1> """ 172 | # assert reg1.num_qubits == reg2.num_qubits, \ 173 | # "registers must have the same number of qubits in order to compute overlap" 174 | 175 | return sum((reg1[state]).conjugate() * reg2[state] for state in reg1) 176 | 177 | def mixedregister_overlap(reg1:MixedRegister, reg2:MixedRegister): 178 | """ returns Tr(reg1.density_matrix()*reg2.density_matrix()) """ 179 | # return np.trace(reg1.density_matrix()@reg2.density_matrix()) 180 | s = 0 181 | for k in range(1 << reg1.num_qubits): 182 | for j in range(1 << reg1.num_qubits): 183 | state_k, state_j = bin(k)[2:], bin(j)[2:] 184 | state_k = "0" * (reg1.num_qubits - len(state_k)) + state_k 185 | state_j = "0" * (reg1.num_qubits - len(state_j)) + state_j 186 | s += reg1.coefficient(state_k, state_j)*reg2.coefficient(state_j, state_k) 187 | return s 188 | 189 | def ket_to_register(ket): 190 | """ 191 | 192 | :param ket: two-dim row vector: the ket vector of a pure state 193 | :return: Register object representing the state 194 | """ 195 | assert abs(sum(abs(x[0])**2 for x in ket)-1) < 1e-10, "Ket not normalized" 196 | num_qubits = int(np.log2(len(ket))) 197 | reg = Register(num_qubits) 198 | reg.pop("0"*num_qubits) 199 | for i in filter(lambda j: ket[j][0], range(1 << num_qubits)): 200 | n = bin(i)[2:] 201 | n = "0"*(num_qubits-len(n)) + n 202 | reg[n] = complex(ket[i][0]) 203 | return reg 204 | 205 | def rho_to_mixedregister(rho): 206 | """ 207 | Take a density matrix `rho` and return the corresponding MixedRegister 208 | object 209 | """ 210 | assert abs(np.trace(rho) - 1) < 1e-10, "Density matrix not normalized " 211 | D, U = np.linalg.eig(rho) 212 | n = len(D) 213 | registers = [ 214 | (D[i], ket_to_register(np.array([[U[j][i]] for j in range(n)]))) 215 | for i in range(n) if D[i] > 1e-10 216 | ] 217 | return MixedRegister(registers) -------------------------------------------------------------------------------- /PythonImplementation/README.md: -------------------------------------------------------------------------------- 1 | To see a (maybe) better Python implementation, please see my other repository qSonify 2 | -------------------------------------------------------------------------------- /PythonImplementation/example0.py: -------------------------------------------------------------------------------- 1 | import QuantumSimulator.register as register 2 | import QuantumSimulator.gates as gates 3 | 4 | 5 | #### create gates 6 | H0 = gates.H(0) # initialize hadamard gate on qubit 0 7 | H1 = gates.H(1) # initialize hadamard gate on qubit 1 8 | CN24 = gates.CX(2, 4) # control qubit = 2, target qubit = 4 9 | R = gates.RZ(2.3, 5) # rotate qubit 5 around z axis by 2.3 radians 10 | T265 = gates.T(2, 6, 5) # tofolli on qubits 2, 6, 5 11 | 12 | 13 | #### initalize register with 7 qubits 14 | reg = register.Register(7) 15 | print("State of register", reg, "\n") 16 | 17 | 18 | #### apply gates. Note that 1, 2, and 3 are equivalent 19 | 20 | ## 1 21 | # reg.apply_gate(H0) 22 | # reg.apply_gate(H1) 23 | # reg.apply_gate(CN24) 24 | # reg.apply_gate(R) 25 | # reg.apply_gate(T265) 26 | 27 | ## 2 28 | # H0(reg) 29 | # H1(reg) 30 | # CN24(reg) 31 | # R(reg) 32 | # T265(reg) 33 | 34 | ## 3 35 | T265(R(CN24(H1(H0(reg))))) 36 | 37 | 38 | #### print the state of the register 39 | print("State of register", reg, "\n") 40 | 41 | 42 | #### measure only qubit 3 and collapse 43 | print("Measured qubit 3 to be", reg.measure_qubit(3)) 44 | print("State of register", reg, "\n") 45 | 46 | 47 | #### measure the entire register and collapse 48 | print("Measured register to be", reg.measure()) 49 | print("State of register", reg) 50 | -------------------------------------------------------------------------------- /PythonImplementation/example1.py: -------------------------------------------------------------------------------- 1 | import QuantumSimulator.register as register 2 | import QuantumSimulator.gates as gates 3 | 4 | 5 | #### Initialize register of 4 qubits 6 | reg = register.Register(4) 7 | print("State of register", reg, "\n") 8 | 9 | 10 | #### Create algorithm. Syntax is 11 | #### G(angle[if necessary], qubit1, qubit2[if necessary], ...) 12 | alg = [ 13 | "H(0)", # Hadamard to qubit 0 14 | "H(1)", # Hadamard to qubit 1 15 | "RZ(pi/2, 2)", # Rotate qubit 2 around z axis by pi/2 radians 16 | "RY(.23*pi, 3)", # Rotate qubit 3 around y axis by .23*pi radians 17 | "T(0, 2, 1)", # Tofolli gate to qubits 0, 2, and 1 18 | "CX(2, 3)", # Controlled-not with control qubit 2 and target qubit 3 19 | "Z(1)", # Pauli-Z gate to qubit 1 20 | "X(2)", # Pauli-X gate to qubit 2 21 | "Y(0)", # Pauli-Y gate to qubit 0 22 | "S(2, 3)" # Swap qubits 2 and 3 23 | ] 24 | 25 | 26 | #### Run through algorithm 27 | reg.apply_algorithm(alg) 28 | 29 | ## Or to go one by one and see the state at each step: 30 | # for gate in alg: 31 | # gates.apply_gate(gate, reg) # apply the next gate to the register 32 | # print("State of register", reg, "\n") 33 | 34 | print("State of register", reg, "\n") 35 | 36 | 37 | #### Find probability to measure 1001 state 38 | print("Probability to measure 1001:", reg.probability("1001"), "\n") 39 | 40 | 41 | #### Measure and collapse register 42 | print("Measured register to be in", reg.measure(), "state") 43 | print("State of register", reg) -------------------------------------------------------------------------------- /PythonImplementation/example2.py: -------------------------------------------------------------------------------- 1 | import QuantumSimulator.register as register 2 | import QuantumSimulator.gates as gates 3 | 4 | 5 | num_qubits = 3 6 | 7 | alg0 = ["H(1)", "CN(0, 1)", "Y(0)", "X(1)", "T(1, 0, 2)", "S(2, 1)", "Y(2)", "P(pi/2, 0)"] 8 | alg1 = ["H(0)", "CN(2, 1)", "Z(0)", "Y(1)", "T(1, 2, 0)", "S(0, 1)", "X(2)", "CP(2.3*pi, 1, 2)"] 9 | 10 | num_gates = len(alg0) 11 | assert num_gates == len(alg1), "algorithms should be the same depth" 12 | 13 | 14 | #### initialize two registers 15 | reg0 = register.Register(num_qubits) 16 | reg1 = register.Register(num_qubits) 17 | 18 | D = 0.0 # D will become the average overlap of the two registers 19 | 20 | for t in range(num_gates): 21 | gates.apply_gate(alg0[t], reg0) # apply t^th gate of alg0 to reg0 22 | gates.apply_gate(alg1[t], reg1) # apply t^th gate of alg1 to reg1 23 | 24 | overlap = register.overlap(reg0, reg1) # = 25 | print(" after applying %d gates:" % t, overlap, "\n") 26 | D += abs(register.overlap(reg0, reg1))**2 27 | 28 | print("State of register 0", reg0, "\n") 29 | print("State of register 1", reg1, "\n") 30 | print("Average overlap of the two registers", D / num_gates) 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quantum-Computer-Simulator-with-Algorithms 2 | *C++11 simulator of quantum registers and quantum algorithms* 3 | 4 | **To see explanations and proofs of the various algorithms, see explanation/math.pdf**. To see examples of how to use this code, look at src/test.cpp. To get started, compile with `g++ -std=c++11 -o test *.cpp`. 5 | 6 | ## QAlgorithms 7 | 8 | I have implemented various algorithms that apply a series of quantum logic gates to a register in order to achieve some goal. There is quantum ripple carry addition, quantum modular arithitic using the quantum and inverse quantum Fourier transforms, Grover's search algorithm, and Shor's factorization algorithm with quantum period finding. See qalgorithms.cpp. 9 | 10 | I am able to simulate many qubits fairly well. Finding the factors of 2813 takes on average under ten seconds (`Shor(2813)`), and 3901 (`Shor(3901)`) took under a minute on my laptop. From this, we see that simulating quantum systems on a classical computer is indeed exponentially complex. 11 | 12 | ## Example usage 13 | 14 | ```C++ 15 | #include "QuantumComputerSimulator.h" 16 | #include 17 | 18 | int main() { 19 | 20 | Register reg(4); // initialize register with 4 qubits 21 | for(int i=0; i<4; i++) reg.Hadamard(i); // apply Hadamard gate to each qubit 22 | reg.ControlledNot(1, 3); // apply Controlled-Not gate to the target qubit 3 with control qubit 1 23 | 24 | QFT(®); // Apply quantum fourier transform to all the qubits. 25 | IQFT(®, 1, 4); // Apply inverse quantum fourier transform to qubits 1 through 3 (4 is non inclusive) 26 | 27 | reg.print_states(); 28 | 29 | // Apply a function to the states. This particular function sends |x> to |x+1 mod 16> 30 | // or, in terms of qubits, |0000> goes to |0001>, |0001> goes to |0010>, ... 31 | // |1111> goes to |0000>. 32 | auto f = [](string state) { 33 | string r = base10_to_binary((binary_to_base10(state) + 1) % (int)pow(2, 4)); 34 | while (r.size() < 4) r = "0" + r; // ensure resulting number is represented with 4 qubits 35 | return r; 36 | }; 37 | // reg.apply_function will check to make sure that the function f represents a unitary transformation. 38 | reg.apply_function(f); 39 | 40 | reg.print_states(); 41 | 42 | char c = reg.measure(0); // measure just qubit 0; 43 | 44 | std::cout << "Measured qubit 0 to be " << c << std::endl; 45 | 46 | // reg will now be collapsed into a superposition of states with the zeroth qubit being equal to c. 47 | std::cout << reg << std::endl; // equivalent to reg.print_states(); 48 | 49 | string state = reg.measure(); // Collapse the whole system. 50 | 51 | std::cout << "Measured the register, collapsed into state " << state << std::endl; 52 | 53 | // reg will now be in state `state` with probability one. 54 | std::cout << reg << std::endl; 55 | 56 | return 0; 57 | } 58 | ``` 59 | 60 | ## Applying your own gates 61 | 62 | See quantum.h/quantum.cpp and unitary.h/unitary.cpp. The Register class has a method to apply_gate(Unitary u, vector v); this applies the gate given by u to the qubits given in v. 63 | 64 | ## A few things that cause weird behavior 65 | 66 | * If you get probabilities that sum to more than one, you probably applied a controlled gate with repeated qubits; i.e. apply a controlled-not gate with control and target both being the same qubit. 67 | * Be very careful with operations involving unsigned ints and ints. I decided to make most types in the code unsigned, but this often causes strange things to happen, and caused me many minutes of confusion. If in doubt, be very explicit; convert everything to an int when doing loops and calculations, and then convert back to unsigned int when sending values into functions. 68 | * When computing modulo powers, use my `mod_power(a, x, N)` function from methods.h instead of `(int)pow(a, x) % N`, because the latter often causes an overflow if a or x are large enough. 69 | 70 | ## Note 71 | 72 | A lot of the algorithms could probably be implemented more efficiently in terms of the code, maybe by combining gates or qubit operations. But for me the point was to understand what one would physically do in order to program a quantum computer. Thus, whenever I can, I only use one- or two-qubit logic gates. 73 | -------------------------------------------------------------------------------- /explanation/math.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtiosue/Quantum-Computer-Simulator-with-Algorithms/0b81558d96cf4127af6f3bf076a17848cd58ccfb/explanation/math.pdf -------------------------------------------------------------------------------- /src/QuantumComputerSimulator.h: -------------------------------------------------------------------------------- 1 | #ifndef QUANTUMCOMPUTERSIMULATOR_INCLUDE 2 | #define QUANTUMCOMPUTERSIMULATOR_INCLUDE 3 | 4 | #include "types.h" 5 | #include "methods.h" 6 | #include "unitary.h" 7 | #include "rand.h" 8 | #include "quantum.h" 9 | #include "qalgorithms.h" 10 | 11 | #endif -------------------------------------------------------------------------------- /src/methods.cpp: -------------------------------------------------------------------------------- 1 | #include "methods.h" 2 | 3 | // Useful functions 4 | 5 | unsigned int char_to_int(char c) { return c - '0'; } 6 | 7 | unsigned int gcd(unsigned int a, unsigned int b) { 8 | // Find the greatest common divisor between a and b. 9 | unsigned int t; 10 | while (b != 0) { t = b; b = a % b; a = t; } 11 | return a; 12 | } 13 | 14 | unsigned int binary_to_base10(std::string s) { 15 | /* 16 | Convert binary number to base 10 with bit shifts. 17 | 18 | This function also does the following: 19 | Find row of matrix corresponding to state 20 | i.e. for 3 qubits, the unitary matrix would be 8x8. 21 | The basis is, in order, 22 | {|000>, |001>, |010>, |011>, |100>, |101>, |110>, |111>} 23 | Thus, if we input the state "000", this function returns 0. 24 | if we input the state "101", this function returns 5. 25 | */ 26 | 27 | 28 | /* RECURSIVE WAY 29 | if (!state.length()) return 0; 30 | int i = 0; 31 | if (state[0] == '1') i = pow(2, state.length()) / 2; 32 | return i + binary_to_base10(state.substr(1, state.length() - 1)); 33 | */ 34 | 35 | // BITSHIFT WAY 36 | unsigned int result = 0; 37 | for (unsigned int i = 0; i < s.length(); i++) { 38 | result ^= (char_to_int(s[i]) << (s.length() - i - 1)); 39 | } 40 | return result; 41 | } 42 | 43 | std::string base10_to_binary(unsigned int x) { 44 | if (x == 0) return "0"; 45 | std::string s = ""; 46 | while (x > 0) { 47 | if (x % 2 == 0) s = "0" + s; 48 | else s = "1" + s; 49 | x /= 2; 50 | } 51 | return s; 52 | } 53 | 54 | unsigned int mod_power(unsigned int a, unsigned int x, unsigned int N) { 55 | /* 56 | For large x, doing pow(a, x) % N causes overflow. So instead we 57 | use this function to compute a^x mod N for large x. 58 | 59 | This function operates on the fact that x^y mod n = (x mod n)^y mod n 60 | (see the paper for proof). 61 | */ 62 | 63 | unsigned int res = 1; 64 | 65 | a = a % N; // Update a if it is more than or equal to N 66 | 67 | while (x > 0) { 68 | // If x is odd, multiply a with result 69 | if (x & 1) res = (res*a) % N; 70 | 71 | // x must be even now 72 | x = x >> 1; // x = floor(x / 2) 73 | a = (a*a) % N; 74 | } 75 | return res; 76 | } 77 | -------------------------------------------------------------------------------- /src/methods.h: -------------------------------------------------------------------------------- 1 | #ifndef METHODS_INCLUDE 2 | #define METHODS_INCLUDE 3 | 4 | #include 5 | 6 | unsigned int char_to_int(char c); 7 | 8 | unsigned int gcd(unsigned int a, unsigned int b); 9 | 10 | unsigned int binary_to_base10(std::string s); 11 | 12 | std::string base10_to_binary(unsigned int x); 13 | 14 | unsigned int mod_power(unsigned int a, unsigned int x, unsigned int N); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/qalgorithms.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "qalgorithms.h" 4 | #include "rand.h" 5 | #include "methods.h" 6 | 7 | #include 8 | 9 | // QUANTUM FOURIER TRANSFORM 10 | 11 | void QFT(Register *reg, unsigned int start, unsigned int end) { 12 | /* 13 | Apply the quantum fourier transform to qubits 14 | start through end - 1. int end is noninclusive. 15 | 16 | start and end are set by default be zero in the header file. 17 | */ 18 | 19 | if (end == 0) end = reg->num_qubits; 20 | 21 | // WITH SINGLE UNITARY TRANSFORMATION 22 | /* 23 | int n = end - start; vec_int v; 24 | for (int i = start; i < end; i++) v.push_back(i); 25 | reg->apply_gate(Unitary::QFT(n), v); 26 | */ 27 | 28 | // REALISTICALLY, WITH SINGLE AND DOUBLE QUBIT GATES 29 | for (unsigned int j = start; j < end; j++) { 30 | reg->Hadamard(j); 31 | for (unsigned int k = 1; k < end - j; k++) { 32 | reg->ControlledPhaseShift(j + k, j, pi/double(1 << k)); // 1 << k is pow(2, k) 33 | } 34 | } 35 | for (unsigned int i = 0; i < floor((end-start) / 2.0); i++) reg->Swap(start+i, end-i-1); 36 | } 37 | 38 | // INVERSE QUANTUM FOURIER TRANSFORM 39 | 40 | void IQFT(Register *reg, unsigned int start, unsigned int end) { 41 | // Just run QFT backwords for qubits start through end - 1. 42 | // start and end are set by default be zero in the header file. 43 | 44 | if (end == 0) end = reg->num_qubits; 45 | 46 | // WITH SINGLE UNITARY TRANSFORMATION 47 | /* 48 | int n = end - start; vec_int v; 49 | for (int i = start; i < end; i++) v.push_back(i); 50 | reg->apply_gate(Unitary::IQFT(n), v); 51 | */ 52 | 53 | // REALISTCALLY, WITH SINGLE AND DOUBLE QUBIT GATES 54 | ///* 55 | for (unsigned int i = 0; i < floor((end-start) / 2.0); i++) reg->Swap(start+i, end-i-1); 56 | 57 | // note: can't use unsigned int's here because unsigned j=-1 58 | // is always greater than zero. NEED to convert end and start to int 59 | // in order to properly compare. 60 | for (int j = int(end) - 1; j >= int(start); j--) { 61 | for (int k = int(end)-j-1; k >= 1; k--) { 62 | // don't need to explicilty convert to unsigned int here, but might as well. 63 | reg->ControlledPhaseShift((unsigned int)(j + k), (unsigned int)j, -pi / double(1 << k)); // 1 << k is pow(2, k) 64 | } 65 | reg->Hadamard((unsigned int)j); 66 | } 67 | //*/ 68 | } 69 | 70 | 71 | // FINDING THE PERIOD OF f(x) = a^x mod N USING THE QUANTUM ALGORITHM 72 | 73 | unsigned int find_Shor_period(unsigned int a, unsigned int N, unsigned int depth_limit) { 74 | /* 75 | Find the period r of the function 76 | f(x) = a^x mod N 77 | using the quantum algorithm 78 | */ 79 | 80 | if (depth_limit <= 0) { 81 | printf("Reached maximum depth limit in period find. Returning 1.\n"); 82 | return 1; 83 | } 84 | 85 | // q is the number of numbers register 1 can hold. 86 | // q must be at least 2*N so that even if the period r = N-1, the first register 87 | // can still hold enough numbers to have at least two x such that f(x0) = f(x1) 88 | // because x0 + r = x1. Most literature I have found says that we should initialize 89 | // q such that N^2 <= q <= 2N^2 so that q / r > N even when r = N - 1, thus 90 | // there will be at least N x such that f(x0) = f(x1) = ... = f(xN) where 91 | // xi = x0 + i*r. But for the code, I've found that q = 2*N works fine. With a smaller 92 | // q, we have a smaller probability of measuring the correct r, so we may have to recurse 93 | // through this function more. But simulating a quanutum register on a classical computer is 94 | // exponential, so recursing through is still more faster than simulating more qubits. 95 | unsigned int q = 2 * N; 96 | 97 | unsigned int L1 = floor(log2(q)) + 1; // number of qubits in register 1 98 | unsigned int L2 = floor(log2(N)) + 1; // number of qubits in register 2 99 | // printf("Initialized register 1 with %d qubits and register 2 with %d\n", L1, L2); 100 | 101 | // This is very important! I messed this up for a while. q is 2^L1. 102 | // What we set earlier was just to help pick L1. 103 | q = (unsigned int)(1 << L1); // equiv to q = (unsigned int)pow(2, L1); 104 | 105 | 106 | Register reg(L1 + L2); 107 | 108 | // Make equal superposition in register 1. 109 | for (unsigned int i = 0; i < L1; i++) reg.Hadamard(i); 110 | // Could have also just QFTed the first L1 qubits. Has same effect. 111 | 112 | 113 | auto f = [a, N, L1, L2](string s) { 114 | /* 115 | Given a state of the two registers |x>|0>, this function 116 | will make it |x>|a^x mod N>. The first register has L1 117 | qubits, the second has L2 qubits. 118 | */ 119 | string t = s.substr(0, L1); 120 | unsigned int x = binary_to_base10(t); 121 | // use my defined mod_power function so that we don't overflow 122 | string res = base10_to_binary(mod_power(a, x, N)); 123 | while (res.size() < L2) res = "0" + res; 124 | return t + res; 125 | }; 126 | // This entangles the registers. Sends |x> to |f(x)>. 127 | // In our case, we define f so that this sends |x, y> to |x, f(x)> 128 | reg.apply_function(f); 129 | 130 | // Don't technically need to measure yet, I don't think. 131 | // Could just wait until the end, but this reduces the number 132 | // of states, and so the QFT will perform faster (on the computer, 133 | // in real life, I don't think this affects the speed). 134 | for (unsigned int i = L1; i < L1 + L2; i++) reg.measure(i); // measure register 2. 135 | 136 | // Quantum fourier transform the first register. 137 | QFT(®, 0, L1); 138 | 139 | // m will be an integer multiple of q / r with high prbability 140 | // where r is the period. 141 | unsigned int m = binary_to_base10(reg.measure().substr(0, L1)); // Measurement of register 1. 142 | 143 | if (m == 0) { 144 | // printf("Quantum period find failed; trying again\n"); 145 | return find_Shor_period(a, N, depth_limit - 1); 146 | } 147 | 148 | // with high probability, m = lambda * q / r for some 149 | // integer lambda where r is the period. Find the period. 150 | // Let c = m / q. We know c = lambda / r. Expand c with 151 | // continuous fractions (see the paper) to find the 152 | // denominator r. This is O(log N) time, I think, so not bad. 153 | 154 | unsigned int r = 0; // If r is still zero afte r the while loop, we've failed. 155 | double c = double(m) / double(q); // this equals lambda / r for some lambda. Find the denomitor = r. 156 | // printf("Beginning continuous fractions expansion to find denominator for %g\n", c); 157 | unsigned int a1, d0, d1, dtemp; // a1 will hold cf expansion coefficients. The d's are denominators 158 | a1 = floor(1 / c); 159 | d0 = 1; d1 = a1; 160 | 161 | // we know the denominator is not greater than q, because lam / r = m / q. 162 | // if c == 0 then we've completed the cf expansion. 163 | // if c > 1, then it means that at some point c was very small, basically zero 164 | // and somehow this caused c = 1.0 / c - floor(1 / c) to not be less than 1. 165 | // Basically, it means we finished the cf expansion and didn't find r. 166 | // This almost never happens when q >= N^2, but happens often when Q = 2*N. 167 | while (c != 0.0 && c <= 1.0 && d1 < q) { 168 | if (!(d1 & 1) && mod_power(a, d1, N) == 1) { r = d1; break; } // Make sure r is even. Found it. 169 | else if (2 * d1 < q && mod_power(a, 2 * d1, N) == 1) { r = 2 * d1; break; } // Try two times, which will be even. 170 | c = 1.0 / c - double(a1); 171 | a1 = floor(1.0 / c); 172 | dtemp = d1; d1 = d1 * a1 + d0; d0 = dtemp; 173 | } 174 | 175 | if (r == 0) { 176 | // printf("Quantum period find failed; trying again\n"); 177 | return find_Shor_period(a, N, depth_limit - 1); 178 | } 179 | 180 | // We already made sure that r was even, so we're good. 181 | 182 | printf("Quantum period find found the period of %d^x mod %d to be %d\n", a, N, r); 183 | return r; 184 | } 185 | 186 | // FACTORIZTION 187 | 188 | unsigned int Shor(unsigned int N, unsigned int depth_limit) { 189 | /* 190 | Find a single factor of N. N must not be an integer 191 | power of a prime. See the notes for an explaination of 192 | how Shor's algorithm works. The quantum computation only 193 | occurs in the find_period function. 194 | 195 | depth_limit is set by default in header. This limits the recursive depth. 196 | 197 | set_srand() must be called before using this function. 198 | */ 199 | if (depth_limit <= 0) { 200 | printf("Reached maximum depth limit in Shor. Try again, or increase depth limit\n"); 201 | return 1; 202 | } 203 | 204 | if (N % 2 == 0) return 2; 205 | unsigned int a = (unsigned int)(floor(get_rand()*(N-1)+1)); if (a == 1) a++; 206 | unsigned int g = gcd(a, N); 207 | if (g != 1 && g != N) { 208 | printf("Completed Shor's algorithm classically. Found a factor of %d to be %d\n", N, g); 209 | printf("But we want to solve it quantumly! So starting over...\n"); 210 | // return g; 211 | return Shor(N, depth_limit); 212 | } 213 | 214 | // printf("Using quantum period finding algorithm to find the period of %d ^ x mod %d\n", a, N); 215 | unsigned int r = find_Shor_period(a, N); unsigned int n = mod_power(a, r / 2, N); 216 | // if (r % 2 == 1 || n % N == N-1) return Shor(N, depth_limit-1); // start over 217 | 218 | unsigned int res = gcd(n - 1, N); 219 | if (res != 1 && res != N) { 220 | printf("Shor found a factor of %d to be %d\n", N, res); 221 | return res; 222 | } 223 | res = gcd(n + 1, N); 224 | if (res != 1 && res != N) { 225 | printf("Shor found a factor of %d to be %d\n", N, res); 226 | return res; 227 | } 228 | 229 | // printf("Shor did not find a factor; trying again\n"); 230 | return Shor(N, depth_limit - 1); 231 | } 232 | 233 | // RIPPLE CARRY ADDITION 234 | 235 | void HalfAdder(unsigned int qubit1, unsigned int qubit2, unsigned int carry, Register *s) { 236 | s->Toffoli(qubit1, qubit2, carry); // Carry (AND) 237 | s->ControlledNot(qubit1, qubit2); // Sum (XOR) 238 | // Sum is stored in qubit2. 239 | } 240 | 241 | void FullAdder(unsigned int qubit1, unsigned int qubit2, unsigned int carryin, unsigned int carryout, Register *s) { 242 | s->Toffoli(qubit1, qubit2, carryout); // Carry (AND) 243 | s->ControlledNot(qubit1, qubit2); // Sum (XOR) 244 | s->Toffoli(qubit2, carryin, carryout); // Carry (AND) 245 | s->ControlledNot(carryin, qubit2); // Sum (XOR) 246 | // Sum is stored in b 247 | } 248 | 249 | void Add(Register *reg) { 250 | /* 251 | Semi-classical ripple-carry adder. 252 | Uses only Controlled-NOT and Toffoli (CC-NOT) gates to compute the sum. 253 | 254 | If the register has 3*n qubits, this function computes the sum of the 255 | number respresented by the first n qubits (0 to n-1) and the number represented by 256 | the second n qubits (n to 2n-1). The extra n qubits (2n to 3n-1) are used to aid 257 | in the computation AND SHOULD BE SET TO ZERO. The last qubit (3*n-1) is the overflow digit. 258 | 259 | This is deterministic if the register is in a particular state, 260 | 261 | i.e. |101011000> is equivalent to 101 + 011 in binary or 5 + 3 262 | in base 10 and will always evaluate to 8. 263 | 264 | but if the register is in a superposition of states, the result is probabilistic. 265 | 266 | NOTE that this method affects the inputed register, but the register will not be measured. 267 | */ 268 | 269 | unsigned int num_bits = reg->num_qubits / 3; // number of bits per number. 270 | 271 | // Half adder on least significant qubit 272 | HalfAdder(num_bits - 1, 2 * num_bits - 1, 2 * num_bits, reg); 273 | 274 | for (unsigned int i = 2; i <= num_bits; i++) 275 | FullAdder(num_bits-i, 2*num_bits-i, 2*num_bits+i-2, 2*num_bits+i-1, reg); 276 | } 277 | 278 | unsigned int Add(unsigned int a, unsigned int b) { 279 | /* 280 | Semi-classical ripple-carry adder. 281 | Uses only Controlled-NOT and Toffoli (CC-NOT) gates to compute the sum. 282 | 283 | Sets up a register so that when calling Add(register) (see the function above) 284 | it deterministically evaluates a+b correctly. 285 | 286 | Example: 287 | If a is 5 == 101 in binary 288 | and b is 12 == 1100 in binary 289 | 290 | then we set up a Register such that it is in the state 291 | |0101 1100 0000> with probability one. The last four qubits 292 | are used to aid in the computation that occurs in the Add(register) 293 | function above. 294 | */ 295 | unsigned int num_bits = (unsigned int)(log2(max(a, b))) + 1; // num of bits per number. 296 | 297 | Register reg(num_bits * 3); 298 | 299 | // Set up the first section of qubits for the integer a 300 | // and the second section of qubits for the integer b. 301 | 302 | unsigned int ta, tb; 303 | for (unsigned int i = 1; i <= num_bits; i++) { 304 | 305 | // Get binary one or zero for this digit of a. 306 | if ((unsigned int)(1 << (num_bits - i)) > a) ta = 0; // 1 << x is pow(2, x) 307 | else {ta = 1; a = a % (unsigned int)(1 << (num_bits - i));} 308 | 309 | // Get binary one or zero for this digit of b. 310 | if ((unsigned int)(1 << (num_bits - i)) > b) tb = 0; 311 | else { tb = 1; b = b % (unsigned int)(1 << (num_bits - i)); } 312 | 313 | // Set them up in the register. 314 | if (ta) reg.PauliX(i-1); 315 | if (tb) reg.PauliX(num_bits+i-1); 316 | } 317 | 318 | Add(®); 319 | 320 | string s = reg.measure(); 321 | // our result is the qubits in the place of number b; i.e. qubits num_bits 322 | // through 2*num_bits - 1. But we also have an overflow at qubit 3*num_bits - 1. 323 | 324 | return binary_to_base10(s.substr(3 * num_bits - 1, 1) + s.substr(num_bits, num_bits)); 325 | } 326 | 327 | // MODULAR ADDITION USING QFT AND IQFT 328 | 329 | void ModAdd(Register *reg) { 330 | /* 331 | For each state in the register, transforms a state 332 | |x, y> 333 | into the state 334 | |(x+y) mod pow(2, reg->num_bits), z> 335 | where we don't care about z. 336 | 337 | x and y must be represented by the same number of bits, so 338 | reg must have an even number of bits. 339 | */ 340 | 341 | unsigned int n = reg->num_qubits / 2; 342 | 343 | QFT(reg, 0, n); // Apply to just the bits representing x. 344 | 345 | // Bits representing y act as controls. 346 | int power; 347 | for (unsigned int control = n; control < reg->num_qubits; control++) { 348 | power = control - n; 349 | for (int target = int(n) - 1; target >= int(2 * n - control) - 1; target--) { 350 | reg->ControlledPhaseShift(control, (unsigned int)target, pi / double(1 << power)); // 1 << power is pow(2, power) 351 | power--; 352 | } 353 | } 354 | 355 | IQFT(reg, 0, n); // Apply to just the bits representing x. 356 | } 357 | 358 | unsigned int ModAdd(unsigned int a, unsigned int b, unsigned int num_bits) { 359 | /* 360 | Compute (a + b) mod pow(2, num_bits) where num_bits is per number. 361 | We set up a Register to be in the state |a b> 362 | where a is reprented in binary with num_bits and likewise 363 | for b. The result is stored on the bits the represent a. 364 | */ 365 | 366 | if (num_bits < (unsigned int)(log2(max(a, b))) + 1) { 367 | printf("Not enough bits to compute %d + %d mod %d\n", a, b, (int)(1 << num_bits)); // 1 << num_bits is pow(2, num_bits) 368 | return 0; 369 | } 370 | 371 | Register reg(num_bits * 2); 372 | 373 | // Set up the first section of qubits for the integer a 374 | // and the second section of qubits for the integer b. 375 | 376 | unsigned int ta, tb; 377 | for (unsigned int i = 1; i <= num_bits; i++) { 378 | 379 | // Get binary one or zero for this digit of a. 380 | if ((unsigned int)(1 << (num_bits - i)) > a) ta = 0; // 1 << x is pow(2, x) 381 | else { ta = 1; a = a % (unsigned int)(1 << (num_bits - i)); } 382 | 383 | // Get binary one or zero for this digit of b. 384 | if ((unsigned int)(1 << (num_bits - i)) > b) tb = 0; 385 | else { tb = 1; b = b % (unsigned int)(1 << (num_bits - i)); } 386 | 387 | // Set them up in the register. 388 | if (ta) reg.PauliX(i - 1); 389 | if (tb) reg.PauliX(num_bits + i - 1); 390 | } 391 | 392 | ModAdd(®); 393 | 394 | // reg will now be in a single state. 395 | 396 | string s = reg.measure(); 397 | 398 | return binary_to_base10(s.substr(0, num_bits)); 399 | } 400 | 401 | // GROVER'S SEARCH 402 | 403 | unsigned int Grover(unsigned int omega, unsigned int num_bits, bool verbose) { 404 | /* 405 | Perform a Grover search to find what omega is given the black box 406 | operator Uomega. Of course, here we know omega and make Uomega from 407 | that, but the principle is that we are given Uomega such that 408 | Uomega |x> = |x> if x != omega 409 | Uomega |x> = -|x> if x == omega 410 | and we want to find omega. This is the simplest appication of the 411 | Grover search. 412 | 413 | omega must be in the range 0 <= omega < pow(2, num_bits). 414 | If verbose is true, the register will be printed prior to the measurement. 415 | set_srand() must be called before calling this function. 416 | */ 417 | 418 | unsigned int N = 1 << num_bits; // 1 << num_bits is pow(2, num_bits) 419 | if (omega >= N) { printf("%d is not enough bits for omega = %d\n", num_bits, omega); return 0; } 420 | 421 | // Make Uomega, our black box operator. 422 | Unitary Uomega = Unitary::Identity(N); Uomega[omega][omega] = -1.0; 423 | 424 | // The Grover diffusion operator, 2|s> 425 | // is equal superposition of all states. 426 | Unitary D(N); 427 | for (unsigned int i = 0; i < D.dimension; i++) { 428 | for (unsigned int j = 0; j < D.dimension; j++) { 429 | D[i][j] = 2.0 / double(N); 430 | if (i == j) D[i][j] -= 1.0; 431 | } 432 | } 433 | 434 | // // Here I define Us such that Hadamard^n * Us * Hadamard^n is the 435 | // // Grover diffusion operator, where Hadamard^n means the Hadamard 436 | // // gate applied to each qubit. 437 | // Unitary Us = Unitary::Identity(N) * -1.0; Us[0][0] = 1.0; 438 | 439 | Register r(num_bits); vec_int v; 440 | 441 | // Begin with equal superposition. 442 | for (unsigned int i = 0; i < r.num_qubits; i++) { r.Hadamard(i); v.push_back(i); } 443 | 444 | // iterate O(sqrt(N)) times. where we stop is important! 445 | for (unsigned int _ = 0; _ < (unsigned int)round(pi / (4.0*asin(1/sqrt(N)))-1.0/2.0); _++) { 446 | // Uomega operator is applied to the whole system 447 | r.apply_gate(Uomega, v); 448 | 449 | // Apply the Grover diffusion operator. 450 | /* 451 | Instead of r.apply_gate(D, v), could do the following, which is more physically realistic I think. 452 | 453 | for (int i = 0; i < r.num_qubits; i++) r.Hadamard(i); 454 | r.apply_gate(Us, v); 455 | for (int i = 0; i < r.num_qubits; i++) r.Hadamard(i); 456 | */ 457 | 458 | r.apply_gate(D, v); 459 | } 460 | 461 | // When printing states, you can see that the basis element 462 | // corresponding to omega has a much higher amplitude than 463 | // the rest of the basis elements. 464 | if(verbose) r.print_states(); 465 | 466 | // Collapse the system. There is a high probability that we get 467 | // the basis element corresponding to omega. 468 | string s = r.measure(); // result 469 | 470 | return binary_to_base10(s); 471 | } 472 | -------------------------------------------------------------------------------- /src/qalgorithms.h: -------------------------------------------------------------------------------- 1 | #ifndef QALGORITHMS_INCLUDE 2 | #define QALGORITHMS_INCLUDE 3 | 4 | #include "quantum.h" 5 | 6 | const double pi = acos(-1.0); 7 | 8 | void QFT(Register *reg, unsigned int start=0, unsigned int end=0); 9 | 10 | void IQFT(Register *reg, unsigned int start=0, unsigned int end=0); 11 | 12 | unsigned int find_Shor_period(unsigned int a, unsigned int N, unsigned int depth_limit=20); 13 | 14 | unsigned int Shor(unsigned int N, unsigned int depth_limit=20); 15 | 16 | void Add(Register *reg); 17 | 18 | unsigned int Add(unsigned int a, unsigned int b); 19 | 20 | void ModAdd(Register *reg); 21 | 22 | unsigned int ModAdd(unsigned int a, unsigned int b, unsigned int N); 23 | 24 | unsigned int Grover(unsigned int omega, unsigned int num_bits, bool verbose=true); 25 | 26 | #endif -------------------------------------------------------------------------------- /src/quantum.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "quantum.h" 3 | #include "rand.h" 4 | #include "qalgorithms.h" 5 | #include "methods.h" 6 | #include 7 | 8 | // Register class 9 | 10 | Register::Register(unsigned int num_qubits) { 11 | /* 12 | By default, initializes vaccum (|000...>) to amplitude 13 | one and the rest zero. This can be adjust with logic 14 | gates, of course, or explicitly with the function 15 | set_nonzero_states. 16 | */ 17 | 18 | this->num_qubits = num_qubits; 19 | states[string(num_qubits, '0')] = amp(1, 0); // total prob 1; 20 | } 21 | 22 | vec_states Register::all_states(unsigned int n) { 23 | /* 24 | returns vector of all the possible n qubit states 25 | IN ORDER, where we choose the basis to be in increasing 26 | order in terms of their binary representation. 27 | i.e. 0..00 < 0..01 < 0..10 < 0..11 < 1..00 < 1..01 < 1..11 28 | */ 29 | vec_states v; 30 | if (n == 1) {v.push_back("0"); v.push_back("1");} 31 | else if(n > 1) { 32 | for (string s : all_states(n - 1)) { 33 | v.push_back(s + "0"); 34 | v.push_back(s + "1"); 35 | } 36 | } 37 | return v; 38 | } 39 | 40 | bool Register::check_state(string state) { 41 | // See if state is in state map. 42 | return states.count(state); 43 | } 44 | 45 | void Register::set_nonzero_states(state_map &s) { 46 | amp total = 0.0; 47 | for (state_map::iterator i = s.begin(); i != s.end(); ++i) { 48 | states[i->first] = i->second; total += pow(abs(i->second), 2); 49 | } 50 | if (total == 0.0) 51 | printf("Bad input: must have at least one state with nonzero amplitude\n"); 52 | else if (total == 1.0) 53 | states = s; 54 | else { // normalize input 55 | states = s; 56 | for (state_map::iterator i = states.begin(); i != states.end(); ++i) 57 | states[i->first] = i->second / sqrt(total); 58 | }; 59 | } 60 | 61 | amp Register::amplitude(string state) { 62 | // Note: not actually physically measureable. 63 | if(check_state(state)) return states[state]; 64 | else return 0; 65 | } 66 | 67 | double Register::probability(string state) { 68 | return pow(abs(amplitude(state)), 2); 69 | } 70 | 71 | string Register::measure() { 72 | /* 73 | Measure the system, and collapse it into a state. 74 | Update the states map to have 1 probability of being 75 | in this state, and return the state. 76 | */ 77 | // Will always return something because there is no way for the total 78 | // probability to not equal 1. 79 | double r = get_rand(); 80 | double total(0.0); string s; 81 | for (state_map::iterator i = states.begin(); i != states.end(); ++i) { 82 | s = i->first; 83 | total += probability(s); 84 | if (r <= total) { // collapse 85 | // get rid of all other states which just take up memory with amp zero. 86 | states = state_map(); 87 | states[s] = 1.0; 88 | return s; 89 | } 90 | } 91 | 92 | // WE SHOULD NEVER MAKE IT OUT HERE. If you use my functions, I make sure 93 | // that the total probability is always 1, so we will never make it out here. 94 | // But, if you adjusted the states manually with the operator[] funcionality 95 | // that I included and you did something wrong, then we may make it out here. 96 | // This is why I recommend you never use the reg[] functionality, but if you do, 97 | // make sure you get the physics correct so that the total probability is always 98 | // one and we never make it out here. In the event that we do, just pretend like 99 | // we measured |000...>. 100 | s = string(num_qubits, '0'); 101 | states = state_map(); 102 | states[s] = 1.0; 103 | return s; 104 | 105 | } 106 | 107 | char Register::measure(unsigned int qubit) { 108 | /* 109 | Measure a qubit, and collapse it. 110 | Update the states map to have 1 probability of having a particular 111 | value for the qubit, and returns the value. 112 | */ 113 | // Will always return something because there is no way for the total 114 | // probability to not equal 1. 115 | 116 | double zero_prob = 0; string s; 117 | for (state_map::iterator i = states.begin(); i != states.end(); ++i) { 118 | s = i->first; 119 | if(s[qubit] == '0') zero_prob += probability(s); 120 | } 121 | 122 | state_map m; char v; amp p; 123 | if (get_rand() < zero_prob) {v = '0'; p = sqrt(zero_prob);} 124 | else { v = '1'; p = sqrt(1 - zero_prob); } 125 | // Resete state map to only states with the measured qubit. 126 | for (state_map::iterator i = states.begin(); i != states.end(); ++i) { 127 | s = i->first; 128 | if (s[qubit] == v) m[s] = states[s] / p; 129 | } 130 | states = m; 131 | return v; 132 | } 133 | 134 | void Register::print_states() { 135 | // Show all nonzero states. 136 | cout << *this; 137 | } 138 | 139 | std::ostream &operator<<(std::ostream &os, Register ®) { 140 | // Show all nonzero states 141 | for (state_map::iterator i = reg.states.begin(); i != reg.states.end(); ++i) 142 | os << "|" << i->first << ">: amp-> " 143 | << (i->second).real() << " + " << (i->second).imag() << "i" 144 | << ", prob-> " << reg.probability(i->first) << "\n"; 145 | return os; 146 | } 147 | 148 | state_map Register::copy_map(state_map &s) { 149 | state_map m; 150 | for (state_map::iterator i = s.begin(); i != s.end(); ++i) { m[i->first] = i->second; } 151 | return m; 152 | } 153 | 154 | vec_states Register::nonzero_states() { 155 | /* 156 | Returns all the keys in the states map; so only 157 | states with nonzero amplitude. 158 | 159 | This function is primarily here to be used with the function 160 | below (operator[]). I HEAVILY RECOMMEND NOT USING THIS OR 161 | THAT FUNCTION. See below for why. 162 | */ 163 | vec_states v; 164 | for (state_map::iterator i = states.begin(); i != states.end(); ++i) 165 | v.push_back(i->first); 166 | return v; 167 | } 168 | 169 | amp & Register::operator[](string state) { 170 | /* 171 | Gives public access to the private states map. We return a reference, 172 | so that one can change the states dictionary from outside the class. 173 | THERE ARE NO CHECKS ON THIS FUNCTION. Anything you change, YOU must 174 | make sure that it acts as a unitary transformation, otherwise probabilites 175 | will fail, and then you can have issues with the measure function. 176 | 177 | I HEAVILY RECOMMEND NOT USING THIS FUNCTIONALITY. I make the states 178 | map private because you should really only be using quantum gates 179 | (unitary transformations) to affect the register, and should not be 180 | directly accessing the states. With that being said, I include this 181 | function as a last option. 182 | 183 | Use: 184 | Register reg(4); 185 | cout << reg["0000"] << endl; 186 | reg["0000"] = 1 / sqrt(2); 187 | reg["1000"] = 1 / sqrt(2); 188 | cout << reg << endl; 189 | */ 190 | return states[state]; 191 | } 192 | 193 | 194 | // Gates 195 | 196 | void Register::apply_gate(Unitary u, vec_int qubits) { 197 | /* 198 | Applys the unitary matrix u to the given qubits in the system. 199 | To get rid of unneccessary memory, if we come across a state with 200 | amplitude zero, remove it from the states map. 201 | 202 | Example: 203 | if vec_int qubits = [0, 2], then expect u.dimension to be 4. 204 | We apply the matrix u to the zeroth and second qubits in the 205 | system. The matrix is represented in the basis 206 | {|00>, |01>, |10>, |11>} 207 | where the first number applies to the zeroth qubit and the 208 | second number applies to the second qubit. 209 | 210 | Example: 211 | if vec_int qubits = [2, 4, 0], then we expect u.dimension to be 8. 212 | We apply the matrix u to the second, fourth, and zeroth qubits in the 213 | system. The matrix is represented in the basis 214 | {|000>, |001>, |010>, |011>, |100>, |101>, |110>, |111>} 215 | where the first number applies to the second qubit, the second 216 | number applies the fourth qubit, and the third number applies 217 | to the zeroth qubit. 218 | 219 | All integers in vec_qubits should be different! I do not perform a check, 220 | but instead assume that the user uses the gates correctly. 221 | */ 222 | 223 | if (u.dimension != (unsigned int)(1 << qubits.size())) { // 1 << qubits.size is pow(2, qubits.size()) 224 | printf("Unitary matrix dimension is not correct to be applied to the inputs qubits\n"); 225 | return; 226 | } 227 | 228 | string state, s; unsigned int r, j; state_map old = copy_map(states); amp c; 229 | vec_states temp_states = all_states(qubits.size()); 230 | for (state_map::iterator i = old.begin(); i != old.end(); ++i) { 231 | state = i->first; s = ""; 232 | for (unsigned int q : qubits) s += state[q]; 233 | 234 | r = binary_to_base10(s); // Find which number basis element s corresponds to. 235 | states[state] -= (1.0 - u[r][r]) * old[state]; 236 | // if (states[state] == 0.0) states.erase(state); // Get rid of it. 237 | if (probability(state) < 1e-16) states.erase(state); // zero beyond machine precision. 238 | 239 | j = 0; 240 | for(string k : temp_states) { 241 | if (j != r) { 242 | s = state; 243 | for (unsigned int l = 0; l < k.size(); l++) s[qubits[l]] = k[l]; 244 | c = u[j][r] * old[state]; 245 | if (check_state(s)) { 246 | states[s] += c; 247 | // if (states[s] == 0.0) states.erase(s); 248 | if (probability(s) < 1e-16) states.erase(s); // zero beyond machine precision. 249 | } else if(c != 0.0) states[s] = c; 250 | } 251 | j++; 252 | } 253 | } 254 | } 255 | 256 | // Common gates listed below. Any gate you want to use that is not 257 | // listed below can be implemented by just creating the unitary operator 258 | // corresponding to the gate and calling the apply_gate function. 259 | 260 | void Register::Hadamard(unsigned int qubit) { 261 | /* 262 | zero -> n-1 qubit indexing. 263 | Hadamard operator on single qubit is 264 | H = ((|0> + |1>) <0| + (|0> - |1>) <1|) / sqrt(2) 265 | */ 266 | vec_int v; v.push_back(qubit); 267 | apply_gate(Unitary::Hadamard(), v); 268 | } 269 | 270 | void Register::PhaseShift(unsigned int qubit, double theta) { 271 | /* 272 | zero -> n-1 qubit indexing. 273 | Phase shift by theta is 274 | P = |1><0| + exp(i theta) |0><1| 275 | */ 276 | vec_int v; v.push_back(qubit); 277 | apply_gate(Unitary::PhaseShift(theta), v); 278 | } 279 | 280 | void Register::PiOverEight(unsigned int qubit) { 281 | // zero->n - 1 qubit indexing. 282 | // PhaseShift(qubit, pi / 4.0); 283 | vec_int v; v.push_back(qubit); 284 | apply_gate(Unitary::PiOverEight(), v); 285 | } 286 | 287 | void Register::PauliX(unsigned int qubit) { 288 | /* 289 | zero->n - 1 qubit indexing. 290 | Pauli-X gates, also the NOT gate, for a single qubit is 291 | PX = |1><0| + |0><1| 292 | */ 293 | vec_int v; v.push_back(qubit); 294 | apply_gate(Unitary::PauliX(), v); 295 | } 296 | 297 | void Register::PauliY(unsigned int qubit) { 298 | /* 299 | zero->n - 1 qubit indexing. 300 | Pauli-Y gate for a single qubit is 301 | PY = i(|1><0| - |0><1|) 302 | */ 303 | vec_int v; v.push_back(qubit); 304 | apply_gate(Unitary::PauliY(), v); 305 | } 306 | 307 | void Register::PauliZ(unsigned int qubit) { 308 | /* 309 | zero->n - 1 qubit indexing. 310 | Pauli-Z gate for a single qubit is 311 | PZ = |1><0| - |0><1| 312 | */ 313 | // PhaseShift(qubit, pi); 314 | vec_int v; v.push_back(qubit); 315 | apply_gate(Unitary::PauliZ(), v); 316 | } 317 | 318 | void Register::ControlledNot(unsigned int control_qubit, unsigned int target_qubit) { 319 | /* 320 | zero -> num_qubits-1 qubit indexing. 321 | ControlledNot gate is just the NOT gate (PauliX) on the target 322 | qubit if the controlled qubit is 1. Otherwise, do nothing. 323 | */ 324 | vec_int v; v.push_back(control_qubit); v.push_back(target_qubit); 325 | apply_gate(Unitary::ControlledNot(), v); 326 | } 327 | 328 | void Register::Toffoli(unsigned int control_qubit1, unsigned int control_qubit2, unsigned int target_qubit) { 329 | /* 330 | zero -> num_qubits-1 qubit indexing. 331 | Toffoli gate, also known as the Controlled-Controlled-Not gate 332 | is just the NOT gate (PauliX) on the target qubit if both the 333 | controlled qubits are 1. Otherwise, do nothing. 334 | */ 335 | vec_int v; v.push_back(control_qubit1); v.push_back(control_qubit2); v.push_back(target_qubit); 336 | apply_gate(Unitary::Toffoli(), v); 337 | } 338 | 339 | void Register::ControlledPhaseShift(unsigned int control_qubit, unsigned int target_qubit, double theta) { 340 | /* 341 | zero -> num_qubits-1 qubit indexing. 342 | Just the phase shift gate on the target qubit if the first qubit is 1. 343 | */ 344 | vec_int v; v.push_back(control_qubit); v.push_back(target_qubit); 345 | apply_gate(Unitary::ControlledPhaseShift(theta), v); 346 | } 347 | 348 | void Register::Swap(unsigned int qubit1, unsigned int qubit2) { 349 | /* 350 | zero -> num_qubits-1 qubit indexing. 351 | Swap qubit1 and qubit2. 352 | */ 353 | 354 | // vec_int v; v.push_back(qubit1); v.push_back(qubit2); 355 | // apply_gate(Unitary::Swap(), v); 356 | ControlledNot(qubit1, qubit2); 357 | ControlledNot(qubit2, qubit1); 358 | ControlledNot(qubit1, qubit2); 359 | } 360 | 361 | void Register::Ising(unsigned int qubit1, unsigned int qubit2, double theta) { 362 | vec_int v; v.push_back(qubit1); v.push_back(qubit2); 363 | apply_gate(Unitary::Ising(theta), v); 364 | } 365 | 366 | 367 | // Sort of a gate 368 | void Register::apply_function(function f) { 369 | /* 370 | Unitary transformation that sends 371 | |x> to |f(x)> 372 | 373 | This one is not technically a gate in the code, but in real life would be 374 | a unitary transformation that could be implemented with a quantum circuit. 375 | 376 | Note that in order for this to be unitary, f(x) must map each x to a unique 377 | f(x). ie. f(x) cannot equal f(y) unless x = y. This is checked in this function. 378 | */ 379 | 380 | state_map old = copy_map(states); string state; states = state_map(); 381 | for (state_map::iterator i = old.begin(); i != old.end(); ++i) { 382 | state = f(i->first); 383 | 384 | if (check_state(state)) { 385 | printf("The provided function does not constitute a unitary transformation.\n"); 386 | states = old; return; // reset. 387 | } 388 | 389 | states[state] = i->second; 390 | } 391 | 392 | } 393 | -------------------------------------------------------------------------------- /src/quantum.h: -------------------------------------------------------------------------------- 1 | #ifndef QUANTUM_INCLUDE 2 | #define QUANTUM_INCLUDE 3 | 4 | #include 5 | // #include "types.h" defined from unitary 6 | #include "unitary.h" 7 | #include 8 | 9 | using namespace std; 10 | 11 | class Register { 12 | 13 | private: 14 | state_map states; 15 | bool check_state(string state); 16 | 17 | public: 18 | unsigned int num_qubits; 19 | static vec_states all_states(unsigned int n); 20 | Register(unsigned int num_qubits); 21 | void set_nonzero_states(state_map &s); 22 | amp amplitude(string state); 23 | double probability(string state); 24 | string measure(); 25 | char measure(unsigned int qubit); 26 | void print_states(); 27 | friend std::ostream &operator<<(std::ostream &os, Register ®); 28 | static state_map copy_map(state_map &s); 29 | vec_states nonzero_states(); 30 | amp & operator[](string state); 31 | 32 | // Gates 33 | void apply_gate(Unitary u, vec_int qubits); 34 | 35 | void Hadamard(unsigned int qubit); 36 | void PhaseShift(unsigned int qubit, double theta); 37 | void PiOverEight(unsigned int qubit); 38 | void PauliX(unsigned int qubit); 39 | void PauliY(unsigned int qubit); 40 | void PauliZ(unsigned int qubit); 41 | void ControlledNot(unsigned int control_qubit, unsigned int target_qubit); 42 | void Toffoli(unsigned int control_qubit1, unsigned int control_qubit2, unsigned int target_qubit); 43 | void ControlledPhaseShift(unsigned int control_qubit, unsigned int target_qubit, double theta); 44 | void Swap(unsigned int qubit1, unsigned int qubit2); 45 | void Ising(unsigned int qubit1, unsigned int qubit2, double theta); 46 | 47 | // Sort of a gate 48 | void apply_function(function f); 49 | }; 50 | 51 | #endif -------------------------------------------------------------------------------- /src/rand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "rand.h" 3 | 4 | // Set up our generator once, and keep calling from it. 5 | 6 | std::random_device rd; // Will be used to obtain a seed for the random number engine 7 | std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() 8 | std::uniform_real_distribution<> dis(0.0, 1.0); 9 | 10 | double get_rand() { 11 | return dis(gen); 12 | } 13 | 14 | // This works better than rand() / RAND_MAX -------------------------------------------------------------------------------- /src/rand.h: -------------------------------------------------------------------------------- 1 | #ifndef RAND_INCLUDE 2 | #define RAND_INCLUDE 3 | 4 | double get_rand(); // double between 0.0 and 1.0. 5 | 6 | #endif -------------------------------------------------------------------------------- /src/test.cpp: -------------------------------------------------------------------------------- 1 | #include "QuantumComputerSimulator.h" 2 | #include 3 | 4 | using namespace std; 5 | 6 | void _test_random() { 7 | cout << "\tTesting random generator..." << endl; 8 | for (unsigned int _ = 0; _ < 10; _++) 9 | cout << "\t\t" << get_rand() << endl; 10 | cout << "Finished testing random\n" << endl; 11 | } 12 | 13 | void _test_unitary() { 14 | cout << "Testing unitary...\n" << endl; cout << endl; 15 | Unitary *u; 16 | 17 | cout << "Check default initialization to zero:" << endl; cout << endl; 18 | u = new Unitary(2); 19 | cout << *u << endl; 20 | 21 | cout << "Check right scalar multiplication: the matrix below should be negative two times the following matrix" << endl; 22 | u = new Unitary(3); 23 | for (unsigned int r = 0; r < u->dimension; r++) { for (unsigned int c = 0; c < u->dimension; c++) { (*u)[r][c] = get_rand(); } } 24 | cout << *u << endl; 25 | *u = (*u)*(-2.0); 26 | cout << *u << endl; 27 | cout << endl; 28 | 29 | cout << "Check left scalar multiplication: the matrix below should be three times the following matrix" << endl; 30 | u = new Unitary(4); 31 | for (unsigned int r = 0; r < u->dimension; r++) { for (unsigned int c = 0; c < u->dimension; c++) { (*u)[r][c] = get_rand(); } } 32 | cout << *u << endl; 33 | *u = 3.0 * (*u); 34 | cout << *u << endl; 35 | cout << endl; 36 | 37 | cout << "Finished testing unitary\n" << endl; 38 | } 39 | 40 | void _test_collapse() { 41 | cout << "Testing collapsing a register...\n" << endl; 42 | unsigned int n; 43 | 44 | n = 3; Register *r = new Register(n); 45 | for (unsigned int i = 0; i < r->num_qubits; i++) { 46 | r->Hadamard(i); 47 | r->PauliY(i); 48 | r->PiOverEight(i); 49 | r->PhaseShift(i, 0.2); 50 | r->ControlledNot(i, (i + 1) % r->num_qubits); 51 | r->Toffoli(i, (i + 1) % r->num_qubits, (i + 2) % r->num_qubits); 52 | } 53 | //r->print_states(); 54 | cout << *r << endl; 55 | cout << "\nMeasure all qubits, got state " << r->measure() << "\n" << endl; 56 | r->print_states(); 57 | cout << endl; 58 | 59 | n = 2; r = new Register(n); 60 | for (unsigned int i = 0; i < r->num_qubits; i++) { 61 | r->Hadamard(i); 62 | r->PauliX(i); 63 | r->PiOverEight(i); 64 | r->PhaseShift(i, 0.7); 65 | r->ControlledNot((i + 1) % r->num_qubits, i); 66 | r->PauliZ(i); 67 | } 68 | r->print_states(); 69 | cout << "\nMeasure 0th qubit, got " << r->measure(0) << "\n" << endl; 70 | r->print_states(); 71 | cout << endl; 72 | 73 | n = 4; r = new Register(n); 74 | for (unsigned int i = 0; i < r->num_qubits - 1; i++) { 75 | r->Hadamard(i); 76 | r->PauliX(i); 77 | r->PiOverEight(i); 78 | r->PhaseShift(i, 0.7); 79 | r->ControlledNot((i + 1) % r->num_qubits, i); 80 | r->PauliZ(i); 81 | } 82 | r->print_states(); 83 | cout << "\nMeasure 2nd qubit, got " << r->measure(2) << "\n" << endl; 84 | r->print_states(); 85 | cout << endl; 86 | 87 | cout << "Finished testing register collapse\n" << endl; 88 | } 89 | 90 | void _test_QFT() { 91 | cout << "Testing quantum Fourier transform..." << endl; 92 | cout << endl; 93 | Register *r; 94 | 95 | r = new Register(3); 96 | cout << *r << endl; 97 | cout << "\tNow apply QFT..." << endl; 98 | QFT(r); 99 | cout << *r << endl; 100 | cout << "\tNow apply inverse QFT..." << endl; 101 | IQFT(r); 102 | cout << *r << endl; 103 | cout << endl; 104 | 105 | cout << "\tNew register" << endl; 106 | cout << endl; 107 | 108 | r = new Register(4); r->Toffoli(0, 2, 3); r->ControlledNot(2, 0); r->Hadamard(1); 109 | cout << *r << endl; 110 | cout << "\tNow apply QFT..." << endl; 111 | QFT(r); 112 | cout << *r << endl; 113 | cout << "\tNow apply inverse QFT..." << endl; 114 | IQFT(r); 115 | cout << *r << endl; 116 | cout << endl; 117 | 118 | cout << "\tNew register" << endl; 119 | cout << endl; 120 | 121 | r = new Register(4); r->Hadamard(0); r->Hadamard(1); r->Hadamard(2); r->Hadamard(3); 122 | r->Toffoli(0, 2, 3); r->ControlledNot(2, 0); 123 | r->PhaseShift(2, 2.1); r->ControlledPhaseShift(2, 1, 4.568); r->Ising(0, 2, 1.05548); 124 | cout << *r << endl; 125 | cout << "\tNow apply inverse QFT..." << endl; 126 | IQFT(r); 127 | cout << *r << endl; 128 | cout << "\tNow apply QFT..." << endl; 129 | QFT(r); 130 | cout << *r << endl; 131 | 132 | cout << "\tNew register" << endl; 133 | cout << endl; 134 | 135 | r = new Register(4); 136 | cout << *r << endl; 137 | cout << "\tNow apply QFT to just qubits 1 and 2..." << endl; 138 | QFT(r, 1, 3); 139 | cout << *r << endl; 140 | cout << "\tNow apply inverse QFT to just 1 and 2..." << endl; 141 | IQFT(r, 1, 3); 142 | cout << *r << endl; 143 | cout << endl; 144 | 145 | cout << "Finished testing QFT and IQFT\n" << endl; 146 | } 147 | 148 | void _test_quantum_add() { 149 | cout << "Testing ripple carry quantum addition..." << endl; cout << endl; 150 | unsigned int a, b; 151 | 152 | a = 7; b = 25; 153 | cout << "\t" << a << "+" << b << " = " << a + b << ", Quantum add gave: " << Add(a, b) << endl; 154 | cout << endl; 155 | 156 | a = 543; b = 7; 157 | cout << "\t" << a << "+" << b << " = " << a + b << ", Quantum add gave: " << Add(a, b) << endl; 158 | cout << endl; 159 | 160 | a = 457; b = 323; 161 | cout << "\t" << a << "+" << b << " = " << a + b << ", Quantum add gave: " << Add(a, b) << endl; 162 | cout << endl; 163 | 164 | cout << "Testing quantum fourier transform modular addition..." << endl; cout << endl; 165 | unsigned int N, n; 166 | 167 | a = 6; b = 8; n = 4; N = 1 << n; 168 | cout << "\t" << a << "+" << b << " mod " << N << " = " << (a + b) % N << ", Quantum mod add gave: " << ModAdd(a, b, n) << endl; 169 | cout << endl; 170 | 171 | a = 50; b = 101; n = 7; N = 1 << n; 172 | cout << "\t" << a << "+" << b << " mod " << N << " = " << (a + b) % N << ", Quantum mod add gave: " << ModAdd(a, b, n) << endl; 173 | cout << endl; 174 | 175 | a = 5; b = 3; n = 7; N = 1 << n; 176 | cout << "\t" << a << "+" << b << " mod " << N << " = " << (a + b) % N << ", Quantum mod add gave: " << ModAdd(a, b, n) << endl; 177 | cout << endl; 178 | 179 | a = 5; b = 3; n = 3; N = 1 << n; 180 | cout << "\t" << a << "+" << b << " mod " << N << " = " << (a + b) % N << ", Quantum mod add gave: " << ModAdd(a, b, n) << endl; 181 | cout << endl; 182 | 183 | cout << "Finished testing quantum addition\n" << endl; 184 | } 185 | 186 | void _test_Grover() { 187 | cout << "Testing Grover search algorithm..." << endl; cout << endl; 188 | unsigned int omega, num_bits, N, result; 189 | 190 | num_bits = 3; N = 1 << num_bits; omega = (unsigned int)(get_rand()*N); result = Grover(omega, num_bits); 191 | cout << "\t" << "want " << omega << ", got " << result << "\n" << endl; cout << endl; 192 | 193 | num_bits = 4; N = 1 << num_bits; omega = (unsigned int)(get_rand()*N); result = Grover(omega, num_bits); 194 | cout << "\t" << "want " << omega << ", got " << result << "\n" << endl; cout << endl; 195 | 196 | num_bits = 5; N = 1 << num_bits; omega = (unsigned int)(get_rand()*N); result = Grover(omega, num_bits); 197 | cout << "\t" << "want " << omega << ", got " << result << "\n" << endl; cout << endl; 198 | 199 | num_bits = 6; N = 1 << num_bits; omega = (unsigned int)(get_rand()*N); result = Grover(omega, num_bits, false); 200 | cout << "\t" << "want " << omega << ", got " << result << "\n" << endl; cout << endl; 201 | 202 | cout << "Finished testing Grover\n" << endl; 203 | } 204 | 205 | void _test_period_find() { 206 | cout << "Testing qantum period finding..." << endl; 207 | unsigned int a, N, r; 208 | 209 | N = 15; a = 7; 210 | cout << "\tLooking for period of " << a << "^x mod " << N << endl; 211 | r = find_Shor_period(a, N); 212 | cout << "\ta = " << a << ", N = " << N << ", quantum result: " << r 213 | << ", gives " << a << "^" << r << " = " << mod_power(a, r, N) << " mod " << N << endl; 214 | cout << endl; 215 | 216 | N = 25; a = 3; 217 | cout << "\tLooking for period of " << a << "^x mod " << N << endl; 218 | r = find_Shor_period(a, N); 219 | cout << "\ta = " << a << ", N = " << N << ", quantum result: " << r 220 | << ", gives " << a << "^" << r << " = " << mod_power(a, r, N) << " mod " << N << endl; 221 | cout << endl; 222 | 223 | N = 39; a = 11; 224 | cout << "\tLooking for period of " << a << "^x mod " << N << endl; 225 | r = find_Shor_period(a, N); 226 | cout << "\ta = " << a << ", N = " << N << ", quantum result: " << r 227 | << ", gives " << a << "^" << r << " = " << mod_power(a, r, N) << " mod " << N << endl; 228 | cout << endl; 229 | 230 | cout << "Finished testing quantum period find\n" << endl; 231 | } 232 | 233 | void _test_Shor_factorization() { 234 | cout << "Testing quantum factorization: Shor's algorithm..." << endl; 235 | cout << endl; 236 | unsigned int a; 237 | 238 | a = 15; cout << "\tLooking for factor of " << a << endl; 239 | cout << "\t" << "a Shor factor of " << a << " is " << Shor(a) << endl; 240 | cout << endl; 241 | 242 | a = 21; cout << "\tLooking for factor of " << a << endl; 243 | cout << "\t" << "a Shor factor of " << a << " is " << Shor(a) << endl; 244 | cout << endl; 245 | 246 | a = 2813; cout << "\tLooking for factor of " << a << endl; 247 | cout << "\t" << "a Shor factor of " << a << " is " << Shor(a) << endl; 248 | cout << endl; 249 | 250 | cout << "Finished testing Shor factorization\n" << endl; 251 | } 252 | 253 | 254 | int main() { 255 | 256 | _test_random(); 257 | _test_unitary(); 258 | _test_collapse(); 259 | _test_QFT(); 260 | _test_quantum_add(); 261 | _test_Grover(); 262 | _test_period_find(); 263 | _test_Shor_factorization(); 264 | 265 | return 0; 266 | } 267 | -------------------------------------------------------------------------------- /src/types.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPES_INCLUDE 2 | #define TYPES_INCLUDE 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef std::complex amp; 10 | typedef std::map state_map; 11 | typedef std::vector vec_int; 12 | typedef std::vector vec_states; 13 | 14 | #endif -------------------------------------------------------------------------------- /src/unitary.cpp: -------------------------------------------------------------------------------- 1 | #include "unitary.h" 2 | #include 3 | 4 | 5 | Unitary::Unitary(unsigned int dimension) { 6 | // Initialize square matrix to zero. 7 | this->dimension = dimension; 8 | 9 | matrix = new amp *[dimension]; 10 | for (unsigned int i = 0; i < dimension; i++) matrix[i] = new amp[dimension]; 11 | /* 12 | for (unsigned int i = 0; i < dimension; i++) { 13 | for (unsigned int j = 0; j < dimension; j++) { 14 | matrix[i][j] = 0.0; 15 | } 16 | } 17 | */ 18 | } 19 | 20 | amp *Unitary::operator[](unsigned int i) { 21 | return matrix[i]; 22 | } 23 | 24 | Unitary Unitary::operator*(amp x) { 25 | Unitary n(dimension); 26 | for (unsigned int r = 0; r < dimension; r++) { 27 | for (unsigned int c = 0; c < dimension; c++) { 28 | n[r][c] = matrix[r][c] * x; 29 | } 30 | } 31 | return n; 32 | } 33 | 34 | // Left scalar multiplication, nonmember function 35 | Unitary operator*(amp x, Unitary &U) { 36 | return U * x; 37 | } 38 | 39 | Unitary Unitary::operator*(Unitary &U) { 40 | Unitary f(dimension); 41 | if (U.dimension != dimension) { 42 | printf("Matrices cannot be multiplied; different dimensions\n"); 43 | return f; 44 | } 45 | for (unsigned int row = 0; row < dimension; row++) { 46 | for (unsigned int column = 0; column < dimension; column++) { 47 | for (unsigned int t = 0; t < dimension; t++) { 48 | f[row][column] += matrix[row][t] * U[t][column]; 49 | } 50 | } 51 | } 52 | return f; 53 | } 54 | 55 | std::ostream &operator<<(std::ostream &os, Unitary &U) { 56 | // Show all nonzero states 57 | for (unsigned int r = 0; r < U.dimension; r++) { 58 | for (unsigned int c = 0; c < U.dimension; c++) { 59 | os << U[r][c] << "\t"; 60 | } 61 | os << "\n"; 62 | } 63 | return os; 64 | } 65 | 66 | void Unitary::print_matrix() { 67 | std::cout << *this; 68 | } 69 | 70 | Unitary Unitary::Identity(unsigned int dimension) { 71 | // Unitary is initialized to all zeros 72 | // I don't think this is compiler specific. 73 | Unitary u(dimension); 74 | for (unsigned int i = 0; i < dimension; i++) u[i][i] = 1; 75 | return u; 76 | } 77 | 78 | Unitary Unitary::Hadamard() { 79 | // H = ((|0> + |1>) <0 | + (|0> - |1>) <1|) / sqrt(2) 80 | Unitary u(2); amp c = 1 / sqrt(2); 81 | u[0][0] = c; u[0][1] = c; u[1][0] = c; u[1][1] = -c; 82 | return u; 83 | } 84 | 85 | Unitary Unitary::PauliX() { 86 | // PX = |1><0| + |0><1| 87 | Unitary u(2); 88 | u[0][1] = 1.0; u[1][0] = 1.0; 89 | return u; 90 | } 91 | 92 | Unitary Unitary::PauliY() { 93 | // PY = i(| 1><0 | -| 0><1 | ) 94 | Unitary u(2); amp c(0.0, 1.0); 95 | u[0][1] = -c; u[1][0] = c; 96 | return u; 97 | } 98 | 99 | Unitary Unitary::PauliZ() { 100 | // PZ = |1><0| - |0><1| 101 | Unitary u(2); 102 | u[0][0] = 1.0; u[1][1] = -1.0; 103 | return u; 104 | } 105 | 106 | Unitary Unitary::PhaseShift(double theta) { 107 | // P = |0><0| +exp(i theta) |1><1| 108 | Unitary u(2); 109 | u[0][0] = 1; u[1][1] = exp(amp(0, theta)); 110 | return u; 111 | } 112 | 113 | Unitary Unitary::PiOverEight() { 114 | // PhaseShift(Pi/4) 115 | Unitary u(2); 116 | u[0][0] = 1; u[1][1] = amp(1, 1) / sqrt(2); 117 | return u; 118 | } 119 | 120 | Unitary Unitary::ControlledNot() { 121 | Unitary u(4); 122 | u[0][0] = 1.0; u[1][1] = 1.0; u[2][3] = 1.0; u[3][2] = 1.0; 123 | return u; 124 | } 125 | 126 | Unitary Unitary::Toffoli() { 127 | Unitary u(8); 128 | for (unsigned int i = 0; i < 6; i++) u[i][i] = 1; 129 | u[6][7] = 1; u[7][6] = 1; 130 | return u; 131 | } 132 | 133 | Unitary Unitary::ControlledPhaseShift(double theta) { 134 | // |00> + |01> + |10> + exp(i theta) |11> 135 | Unitary u(4); 136 | u[0][0] = 1.0; u[1][1] = 1.0; u[2][2] = 1.0; u[3][3] = exp(amp(0, theta)); 137 | return u; 138 | } 139 | 140 | Unitary Unitary::Swap() { 141 | /* 142 | Swapping q1 and q2 is equivalent to 143 | ControlledNot(q1, q2), ControlledNot(q2, q1), ControlledNot(q1, q2) 144 | */ 145 | Unitary u(4); 146 | u[0][0] = 1.0; u[3][3] = 1.0; u[1][2] = 1.0; u[2][1] = 1.0; 147 | return u; 148 | } 149 | 150 | Unitary Unitary::Ising(double theta) { 151 | Unitary u(4); amp s = 1.0 / sqrt(2); 152 | for (unsigned int i = 0; i < 4; i++) u[i][i] = s; 153 | u[1][2] = s * amp(0, -1); u[2][1] = s * amp(0, -1); 154 | // acos(-1) is pi. 155 | u[0][3] = s * exp(amp(0, 1)*(theta - acos(-1.0) / 2.0)); 156 | u[3][0] = s * exp(amp(0, 1)*(-theta - acos(-1.0) / 2.0)); 157 | return u; 158 | } 159 | 160 | Unitary Unitary::QFT(unsigned int num_qubits) { 161 | // Makes the matrix that transforms the system. 162 | unsigned int N = 1 << num_qubits; Unitary u(N); // 1 << num_qubits is pow(2, qubits) 163 | amp omega = exp(2*acos(-1.)*amp(0, 1) / double(N)); // acos(-1) is pi. 164 | amp c = 1 / sqrt(N); 165 | for (unsigned int i = 0; i < N; i++) { 166 | for (unsigned int j = 0; j < N; j++) { 167 | u[i][j] = pow(omega, i*j) * c; 168 | } 169 | } 170 | return u; 171 | } 172 | 173 | Unitary Unitary::IQFT(unsigned int num_qubits) { 174 | // Complex transpose of the QFT. 175 | unsigned int N = 1 << num_qubits; Unitary u(N); 176 | amp omega = exp(2 * acos(-1.)*amp(0, -1) / double(N)); // acos(-1) is pi. 177 | amp c = 1 / sqrt(N); 178 | for (unsigned int i = 0; i < N; i++) { 179 | for (unsigned int j = 0; j < N; j++) { 180 | u[i][j] = pow(omega, i*j) * c; 181 | } 182 | } 183 | return u; 184 | } 185 | -------------------------------------------------------------------------------- /src/unitary.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITARY_INCLUDE 2 | #define UNITARY_INCLUDE 3 | 4 | #include "types.h" 5 | 6 | class Unitary { 7 | 8 | private: 9 | amp **matrix; 10 | 11 | public: 12 | unsigned int dimension; 13 | Unitary(unsigned int dimension); 14 | amp * operator[](unsigned int i); 15 | Unitary operator*(amp x); 16 | Unitary operator*(Unitary &U); 17 | void print_matrix(); 18 | friend std::ostream &operator<<(std::ostream &os, Unitary &U); 19 | 20 | static Unitary Identity(unsigned int dimension); 21 | static Unitary Hadamard(); 22 | static Unitary PauliX(); 23 | static Unitary PauliY(); 24 | static Unitary PauliZ(); 25 | static Unitary PhaseShift(double theta); 26 | static Unitary PiOverEight(); 27 | static Unitary ControlledNot(); 28 | static Unitary Toffoli(); 29 | static Unitary ControlledPhaseShift(double theta); 30 | static Unitary Swap(); 31 | static Unitary Ising(double theta); 32 | static Unitary QFT(unsigned int num_qubits); 33 | static Unitary IQFT(unsigned int num_qubits); 34 | 35 | // Matrices to be created upon initialization. 36 | // const static Unitary MHadamard; 37 | 38 | }; 39 | 40 | // For left multiplication. 41 | Unitary operator*(amp x, Unitary &U); 42 | 43 | #endif --------------------------------------------------------------------------------