diff --git a/NQubitSystem.py b/NQubitSystem.py index 4058c16..94abd92 100644 --- a/NQubitSystem.py +++ b/NQubitSystem.py @@ -4,8 +4,11 @@ import matplotlib.pyplot as plt import random import json +import qiskit.quantum_info as qi # The n-qubit system's state is as an array of 2^n coefficients (one for each possible value of the qubits). + + class NQubitSystem: def is_valid_state(self, tolerance=1e-10): # Ensure it's a column vector (shape is (n, 1) or (n,)) @@ -38,7 +41,7 @@ def print_all_gates_applied(self): idx, gate_name, qubits_affected, single_gate, system_gate = gate_applied print(f"Step {idx}: {gate_name} on qubits {qubits_affected}") print("") - + def complex_encoder(self, z): return {"real": float(z.real), "imag": float(z.imag)} @@ -55,8 +58,10 @@ def export_circuit(self, file_path): for gate_applied in self.gates_applied: idx, gate_name, qubits_affected, single_gate, system_gate = gate_applied - single_gate = np.array([[self.complex_encoder(z) for z in row] for row in single_gate]).tolist() - system_gate = np.array([[self.complex_encoder(z) for z in row] for row in system_gate]).tolist() + single_gate = np.array([[self.complex_encoder(z) + for z in row] for row in single_gate]).tolist() + system_gate = np.array([[self.complex_encoder(z) + for z in row] for row in system_gate]).tolist() circuit["gates_applied"].append({ "idx": idx, @@ -76,7 +81,7 @@ def import_circuit(file_path): with open(file_path, "r") as json_file: circuit = json.load(json_file) initial_state = circuit["initial_state"] - quantum_system = NQubitSystem(n_qubits = len(initial_state)) + quantum_system = NQubitSystem(n_qubits=len(initial_state)) quantum_system.initialize_state(initial_state) gates_applied = circuit["gates_applied"] for gate_applied in gates_applied: @@ -84,10 +89,13 @@ def import_circuit(file_path): gate_name = gate_applied["gate_name"] qubits_affected = gate_applied["qubits_affected"] single_gate = gate_applied["single_gate"] - single_gate = np.array([[quantum_system.complex_decoder(d) for d in row] for row in single_gate]) + single_gate = np.array( + [[quantum_system.complex_decoder(d) for d in row] for row in single_gate]) system_gate = gate_applied["system_gate"] - system_gate = np.array([[quantum_system.complex_decoder(d) for d in row] for row in system_gate]) - quantum_system.apply_full_gate(idx, gate_name, qubits_affected, single_gate, system_gate) + system_gate = np.array( + [[quantum_system.complex_decoder(d) for d in row] for row in system_gate]) + quantum_system.apply_full_gate( + idx, gate_name, qubits_affected, single_gate, system_gate) return quantum_system @@ -95,8 +103,9 @@ def import_circuit(file_path): def __init__(self, n_qubits): if n_qubits <= 0: raise ValueError("Number of qubits must be a positive integer") + self.index = 0 self.n_qubits = n_qubits - self.state = np.zeros(2**n_qubits, dtype = complex) + self.state = np.zeros(2**n_qubits, dtype=complex) self.state[0] = 1.0 # Initialize to |000...0> assert self.is_valid_state() self.gates_applied = [] @@ -104,38 +113,42 @@ def __init__(self, n_qubits): # The function receives an array of desired value for each qubit, e.g. `[0,1,0,0]` for a 4-qubit system and sets the state accordingly. def initialize_state(self, qubit_values): assert len(qubit_values) == self.n_qubits - index = int(''.join(map(str, qubit_values)), 2) # Creates a string from the array, e.g. [0,1,1] -> "011" then converts from binary to int to get the position + # Creates a string from the array, e.g. [0,1,1] -> "011" then converts from binary to int to get the position + self.index = int(''.join(map(str, qubit_values)), 2) self.state = np.zeros(2 ** self.n_qubits, dtype=complex) - self.state[index] = 1.0 + self.state[self.index] = 1.0 assert self.is_valid_state() self.initial_state = qubit_values def print_initial_qubits(self): - print(f"Initial qubits (1st qubit starts from the left): {self.initial_state}") + print( + f"Initial qubits (1st qubit starts from the left): {self.initial_state}") def quantum_noise(self): - p = float(input("Probability for noise:")) - target = random.randrange(self.n_qubits) - if random.random() < p: - self.apply_X_gate(target, False) - if random.random() < p: - self.apply_Z_gate(target, False) - - def apply_full_gate(self, idx, gate_name, qubits_affected, single_gate, system_gate, noise = False): + p = float(input("Probability for noise:")) + target = random.randrange(self.n_qubits) + if random.random() < p: + self.apply_X_gate(target, False) + if random.random() < p: + self.apply_Z_gate(target, False) + + def apply_full_gate(self, idx, gate_name, qubits_affected, single_gate, system_gate, noise=False): self.state = np.dot(system_gate, self.state) - + if noise == True: self.quantum_noise() assert self.is_valid_state() - self.gates_applied.append((idx, gate_name, qubits_affected, single_gate, system_gate)) + self.gates_applied.append( + (idx, gate_name, qubits_affected, single_gate, system_gate)) # Apply general gate to the state - def apply_gate(self, gate, n_gate = -1, starting_qubit = 0, noise = False): + def apply_gate(self, gate, n_gate=-1, starting_qubit=0, noise=False): assert 0 <= starting_qubit <= self.n_qubits - n_gate if n_gate == -1: - n_gate = self.n_qubits + n_gate = int(np.log2([len(gate)])[0]) + # self.n_qubits I = np.eye(2) gate_matrix = 1 @@ -149,7 +162,7 @@ def apply_gate(self, gate, n_gate = -1, starting_qubit = 0, noise = False): else: gate_matrix = np.kron(gate_matrix, I) qubit += 1 - + # Update the state by applying the gate matrix self.state = np.dot(gate_matrix, self.state) @@ -159,81 +172,83 @@ def apply_gate(self, gate, n_gate = -1, starting_qubit = 0, noise = False): assert self.is_valid_state() qubits_affected = [starting_qubit + i for i in range(n_gate)] - gate_name = [key for key, value in gates_map.items() if gate.shape == value[0].shape and np.all(value[0] == gate)][0] - #self.gates_applied.append((len(self.gates_applied), gate_name, gate, qubits_affected, gate_matrix)) - self.gates_applied.append((len(self.gates_applied)+1, gate_name, qubits_affected, gate, gate_matrix)) + gate_name = [key for key, value in gates_map.items( + ) if gate.shape == value[0].shape and np.all(value[0] == gate)][0] + # self.gates_applied.append((len(self.gates_applied), gate_name, gate, qubits_affected, gate_matrix)) + self.gates_applied.append( + (len(self.gates_applied)+1, gate_name, qubits_affected, gate, gate_matrix)) - def apply_H_gate(self, target_qubit, noise = False): + def apply_H_gate(self, target_qubit, noise=False): gate = gates_map["H"][0] n_gate = gates_map["H"][1] self.apply_gate(gate, n_gate, target_qubit, noise) - def apply_X_gate(self, target_qubit, noise = False): + def apply_X_gate(self, target_qubit, noise=False): gate = gates_map["X"][0] n_gate = gates_map["X"][1] self.apply_gate(gate, n_gate, target_qubit, noise) - - def apply_Y_gate(self, target_qubit, noise = False): + + def apply_Y_gate(self, target_qubit, noise=False): gate = gates_map["Y"][0] n_gate = gates_map["Y"][1] self.apply_gate(gate, n_gate, target_qubit, noise) - - def apply_Z_gate(self, target_qubit, noise = False): + + def apply_Z_gate(self, target_qubit, noise=False): gate = gates_map["Z"][0] n_gate = gates_map["Z"][1] self.apply_gate(gate, n_gate, target_qubit, noise) - - def apply_T_gate(self, target_qubit, noise = False): + + def apply_T_gate(self, target_qubit, noise=False): gate = gates_map["T"][0] n_gate = gates_map["T"][1] self.apply_gate(gate, n_gate, target_qubit, noise) - - def apply_S_gate(self, target_qubit, noise = False): + + def apply_S_gate(self, target_qubit, noise=False): gate = gates_map["S"][0] n_gate = gates_map["S"][1] self.apply_gate(gate, n_gate, target_qubit, noise) - - def apply_CNOT_gate(self, target_qubit, noise = False): + + def apply_CNOT_gate(self, target_qubit, noise=False): gate = gates_map["CNOT"][0] n_gate = gates_map["CNOT"][1] self.apply_gate(gate, n_gate, target_qubit, noise) - - def apply_CH_gate(self, target_qubit, noise = False): + + def apply_CH_gate(self, target_qubit, noise=False): gate = gates_map["CH"][0] n_gate = gates_map["CH"][1] self.apply_gate(gate, n_gate, target_qubit, noise) - - def apply_CY_gate(self, target_qubit, noise = False): + + def apply_CY_gate(self, target_qubit, noise=False): gate = gates_map["CY"][0] n_gate = gates_map["CY"][1] self.apply_gate(gate, n_gate, target_qubit, noise) - - def apply_CZ_gate(self, target_qubit, noise = False): + + def apply_CZ_gate(self, target_qubit, noise=False): gate = gates_map["CZ"][0] n_gate = gates_map["CZ"][1] self.apply_gate(gate, n_gate, target_qubit, noise) - - def apply_CT_gate(self, target_qubit, noise = False): + + def apply_CT_gate(self, target_qubit, noise=False): gate = gates_map["CT"][0] n_gate = gates_map["CT"][1] self.apply_gate(gate, n_gate, target_qubit, noise) - - def apply_CS_gate(self, target_qubit, noise = False): + + def apply_CS_gate(self, target_qubit, noise=False): gate = gates_map["CS"][0] n_gate = gates_map["CS"][1] self.apply_gate(gate, n_gate, target_qubit, noise) - - def apply_SWAP_gate(self, target_qubit, noise = False): + + def apply_SWAP_gate(self, target_qubit, noise=False): gate = gates_map["SWAP"][0] n_gate = gates_map["SWAP"][1] self.apply_gate(gate, n_gate, target_qubit, noise) - - def apply_CNOT10_gate(self, target_qubit, noise = False): + + def apply_CNOT10_gate(self, target_qubit, noise=False): gate = gates_map["CNOT10"][0] n_gate = gates_map["CNOT10"][1] self.apply_gate(gate, n_gate, target_qubit, noise) - - def apply_TOFFOLI_gate(self, target_qubit, noise = False): + + def apply_TOFFOLI_gate(self, target_qubit, noise=False): gate = gates_map["TOFFOLI"][0] n_gate = gates_map["TOFFOLI"][1] self.apply_gate(gate, n_gate, target_qubit, noise) @@ -246,48 +261,49 @@ def plot_state_probabilities(self): data[key] = value courses = list(data.keys()) values = list(data.values()) - - fig = plt.figure(figsize = (8, 4)) - + + fig = plt.figure(figsize=(8, 4)) + # creating the bar plot - plt.bar(courses, values, color ='maroon', - width = 0.4) - + plt.bar(courses, values, color='maroon', + width=0.4) + plt.xlabel("qubit number") plt.ylabel("probabilities") plt.title("probabilities of measuring 0 in computational basis") plt.show() def produce_measurement(self): - projectors=[np.array([[1,0],[0,0]]), np.array([[0,0],[0,1]]) ] - - def project(i,j,self): + projectors = [np.array([[1, 0], [0, 0]]), np.array([[0, 0], [0, 1]])] + + def project(i, j, self): shape_tuple = () for q in range(self.n_qubits): shape_tuple = shape_tuple + (2,) - - modified_state=np.reshape(self.state, shape_tuple) - projected=np.tensordot(projectors[j],modified_state,(1,i)) - return np.moveaxis(projected,0,i) - + + modified_state = np.reshape(self.state, shape_tuple) + projected = np.tensordot(projectors[j], modified_state, (1, i)) + return np.moveaxis(projected, 0, i) + measurements = np.zeros(self.n_qubits, dtype=int) self.probabilities = np.zeros(self.n_qubits, dtype=float) for i in range(self.n_qubits): - projected=project(i,0,self) - #print(projected) - norm_projected=norm(projected.flatten()) - #measurements = np.zeros(self.n_qubits, dtype=int) - print("No qubit {}. Probability to be 0: {}".format(i, norm_projected**2)) + projected = project(i, 0, self) + # print(projected) + norm_projected = norm(projected.flatten()) + # measurements = np.zeros(self.n_qubits, dtype=int) + print("No qubit {}. Probability to be 0: {}".format( + i, norm_projected**2)) self.probabilities[i] = norm_projected**2 - if np.random.random()control_qubit): - M1 = np.kron(P0, np.eye(2 ** (target_qubit - control_qubit))) - M2 = np.kron(np.kron(P1, np.eye(2 ** (target_qubit - control_qubit) - len(gate_matrix))), gate_matrix) - + if (target_qubit > control_qubit): + M1 = np.kron(P0, np.eye( + 2 ** (target_qubit - control_qubit - 1 + int(np.log2([len(gate_matrix)])[0])))) + + # M1 = np.kron(np.eye(2**(control_qubit)), M1) + # M1 = np.kron(M1, np.eye(2** (self.n_qubits - target_qubit - 1))) + + M2 = np.kron( + np.kron(P1, np.eye(2 ** (target_qubit - control_qubit - 1))), gate_matrix) + # - int(np.log2([len(gate_matrix)])[0]) + # M2 = np.kron(np.eye(2**(control_qubit)), M2) + # M2 = np.kron(M2, np.eye(2** (self.n_qubits - target_qubit - 1))) + else: - M1 = np.kron(np.eye(2 ** (control_qubit - target_qubit)), P0) - M2 = np.kron(gate_matrix, np.kron(np.eye(2 ** (control_qubit - target_qubit -1) - len(gate_matrix)), P1)) + M1 = np.kron(np.eye(2 ** (control_qubit - target_qubit - + 1 + int(np.log2([len(gate_matrix)])[0]))), P0) + # M1 = np.kron(np.eye(2**(target_qubit)), M1) + # M1 = np.kron(M1, np.eye(2** (self.n_qubits - control_qubit - 1))) + + M2 = np.kron(gate_matrix, + np.kron(np.eye(2 ** (control_qubit - target_qubit - 1)), P1)) + # M2 = np.kron(np.eye(2**(target_qubit)), M2) + # M2 = np.kron(M2, np.eye(2** (self.n_qubits - control_qubit - 1))) controlled_gate = M1 + M2 - #print(controlled_gate) + + return controlled_gate + + def control_gate(self, control_qubits, target_qubit, gate_matrix, name=-1): + + control_qubits = np.sort(control_qubits) + index_target = np.searchsorted(control_qubits, target_qubit) + index_target2 = np.searchsorted( + control_qubits, target_qubit + int(np.log2([len(gate_matrix)])[0]) - 1) + assert index_target == index_target2, "Qubit overlap (target qubit same as control qubit)" + + qubits_before_target = control_qubits[:index_target] + qubits_after_target = control_qubits[index_target:] + + controlled_gate = gate_matrix + num_qubits_a = len(qubits_after_target) + for i in range(0, num_qubits_a): + controlled_gate = self.apply_control( + qubits_after_target[i], target_qubit, controlled_gate) + + num_qubits_b = len(qubits_before_target) + for i in range(0, num_qubits_b): + control_qb = qubits_before_target[num_qubits_b - i - 1] + controlled_gate = self.apply_control( + control_qb, target_qubit, controlled_gate) + target_qubit = control_qb + + print(controlled_gate) if name != -1: - gates_map[name] = (controlled_gate, int(np.log2(len(controlled_gate)))) + gates_map[name] = (controlled_gate, int( + np.log2(len(controlled_gate)))) + + qubits_affected = np.append(control_qubits, target_qubit) return controlled_gate + + def swap_n_gate(self, control_qubit, target_qubit, name=-1): + + M1 = np.eye(1) + M2 = np.eye(1) + + if (target_qubit < control_qubit): + aux = target_qubit + target_qubit = control_qubit + control_qubit = aux + + # M1 eye + M1 = np.eye(2 ** (self.n_qubits)) + + # M2 X gates_map["X"][0] + M2_1 = np.kron(np.eye(2 ** control_qubit), gates_map["X"][0]) + M2_2 = np.kron( + np.eye(2 ** (target_qubit - control_qubit - 1)), gates_map["X"][0]) + M2_3 = np.kron(M2_1, M2_2) + M2 = np.kron(M2_3, np.eye(2**(self.n_qubits - target_qubit - 1))) + + # M3 Y gates_map["Y"][0] + M3_1 = np.kron(np.eye(2 ** control_qubit), gates_map["Y"][0]) + M3_2 = np.kron( + np.eye(2 ** (target_qubit - control_qubit - 1)), gates_map["Y"][0]) + M3_3 = np.kron(M3_1, M3_2) + M3 = np.kron(M3_3, np.eye(2**(self.n_qubits - target_qubit - 1))) + + # M4 Z gates_map["Z"][0] + M4_1 = np.kron(np.eye(2 ** control_qubit), gates_map["Z"][0]) + M4_2 = np.kron( + np.eye(2 ** (target_qubit - control_qubit - 1)), gates_map["Z"][0]) + M4_3 = np.kron(M4_1, M4_2) + M4 = np.kron(M4_3, np.eye(2**(self.n_qubits - target_qubit - 1))) + + swap_gate = M1 + M2 + M3 + M4 + swap_gate = swap_gate * 0.5 + print(swap_gate) + if name != -1: + gates_map[name] = (swap_gate, int(np.log2(len(swap_gate)))) + + return swap_gate + + def import_to_qiskit(self): + pass + """ + circuit = json.load(json_file) + initial_state = circuit["initial_state"] + quantum_system = NQubitSystem(n_qubits = len(initial_state)) + quantum_system.initialize_state(initial_state) + gates_applied = circuit["gates_applied"] + for gate_applied in gates_applied: + idx = gate_applied["idx"] + gate_name = gate_applied["gate_name"] + qubits_affected = gate_applied["qubits_affected"] + single_gate = gate_applied["single_gate"] + single_gate = np.array([[quantum_system.complex_decoder(d) for d in row] for row in single_gate]) + system_gate = gate_applied["system_gate"] + system_gate = np.array([[quantum_system.complex_decoder(d) for d in row] for row in system_gate]) + quantum_system.apply_full_gate(idx, gate_name, qubits_affected, single_gate, system_gate) + """ + + def calculate_density_matrix(self): + density_matrix = np.tensordot(self.state, self.state.conj(), axes=0) + return density_matrix + + def plot_density_matrix(self): + density_matrix = np.tensordot(self.state, self.state.conj(), axes=0) + density_matrix = abs(density_matrix) + plt.imshow(density_matrix, cmap='viridis') + plt.colorbar() + plt.title('Density matrix') + plt.show() diff --git a/TestNQubitSystem.py b/TestNQubitSystem.py index ba4f647..8bec00f 100644 --- a/TestNQubitSystem.py +++ b/TestNQubitSystem.py @@ -130,11 +130,13 @@ def test_basic_gates(): print("Apply control gate | Test custom control-gate | Apply CS gate on control-qubit 0 and target-qubit 2!\n") target_qubit = 2 - control_qubit = 0 + control_qubit = [0] + # starting_qubit = np.min([target_qubit,np.min(control_qubit)]) + starting_qubit = 0 gate_name = "S" control_gate_name = f"Controlled-{gate_name}_Cq{control_qubit}_Tq{target_qubit}" - gate = quantum_system.control_gate(control_qubit = control_qubit, target_qubit = target_qubit, gate_matrix = gates_map[gate_name][0], name=control_gate_name) - quantum_system.apply_gate(gate) + gate = quantum_system.control_gate(control_qubits = control_qubit, target_qubit = target_qubit, gate_matrix = gates_map[gate_name][0], name=control_gate_name) + quantum_system.apply_gate(gate, starting_qubit = starting_qubit) quantum_system.print_state() assert np.allclose(quantum_system.state, [0, 0.5, -0.35355, +0.35355j, -0.25-0.25j, -0.35355j, -0.5j, 0]) # https://algassert.com/quirk#circuit=%7B%22cols%22%3A%5B%5B%22H%22%2C%22X%22%2C%22H%22%5D%2C%5B%22Z%22%2C%22Y%22%2C%22Z%5E%C2%BD%22%5D%2C%5B%22Z%5E%C2%BC%22%5D%2C%5B%22X%22%2C%22%E2%80%A2%22%5D%2C%5B1%2C%22H%22%2C%22%E2%80%A2%22%5D%2C%5B%22Y%22%2C%22%E2%80%A2%22%5D%2C%5B1%2C%22Z%22%2C%22%E2%80%A2%22%5D%2C%5B%22Z%5E%C2%BC%22%2C%22%E2%80%A2%22%5D%2C%5B1%2C%22Z%5E%C2%BD%22%2C%22%E2%80%A2%22%5D%2C%5B%22Swap%22%2C%22Swap%22%5D%2C%5B1%2C%22%E2%80%A2%22%2C%22X%22%5D%2C%5B%22X%22%2C%22%E2%80%A2%22%2C%22%E2%80%A2%22%5D%2C%5B%22Z%5E%C2%BD%22%2C1%2C%22%E2%80%A2%22%5D%5D%2C%22init%22%3A%5B1%2C1%5D%7D @@ -149,8 +151,9 @@ def test_basic_gates(): def test_import_export_gate(): gate = gates_map["TOFFOLI"][0] - Gate.export_gate('test', gate) - toffoli_gate = Gate.import_gate('test') + file_path = 'tests/gate' + Gate.export_gate(file_path, gate) + toffoli_gate = Gate.import_gate(file_path) assert np.allclose(toffoli_gate, gates_map["TOFFOLI"][0]) print("Test import/export successfull!") @@ -169,11 +172,13 @@ def test_custom_gate(): print("\nApply CT gate on control-qubit 0 and target-qubit 2!\n") target_qubit = 2 - control_qubit = 0 + control_qubit = [0] gate_name = "T" + starting_qubit = 0 + # np.min([target_qubit,np.min(control_qubit)]) control_gate_name = f"Controlled-{gate_name}_Cq{control_qubit}_Tq{target_qubit}" - gate = quantum_system.control_gate(control_qubit = control_qubit, target_qubit = target_qubit, gate_matrix = gates_map[gate_name][0], name=control_gate_name) - quantum_system.apply_gate(gate) + gate = quantum_system.control_gate(control_qubits = control_qubit, target_qubit = target_qubit, gate_matrix = gates_map[gate_name][0], name=control_gate_name) + quantum_system.apply_gate(gate, starting_qubit = starting_qubit) quantum_system.print_state() assert np.allclose(quantum_system.state, [0, 0, 0, 1/np.sqrt(2), 0, 0, 0, 0.5+0.5j]) # https://algassert.com/quirk#circuit=%7B%22cols%22%3A%5B%5B1%2C1%2C%22H%22%5D%2C%5B%22Z%5E%C2%BC%22%2C1%2C%22%E2%80%A2%22%5D%5D%2C%22init%22%3A%5B1%2C1%5D%7D @@ -260,12 +265,83 @@ def test_noise(): else: print("Quantum noise applied!") +def test_lab3_circuit(): + quantum_system = NQubitSystem(n_qubits = 4) + quantum_system.initialize_state([0,1,1,0]) + quantum_system.print_state() + quantum_system.print_initial_qubits() + + quantum_system.apply_H_gate(3) + quantum_system.print_state() + quantum_system.print_all_gates_applied() + + quantum_system.apply_X_gate(1) + quantum_system.print_state() + quantum_system.print_all_gates_applied() + + quantum_system.apply_S_gate(3) + quantum_system.print_state() + quantum_system.print_all_gates_applied() + + quantum_system.apply_SWAP_gate(2) + quantum_system.print_state() + quantum_system.print_all_gates_applied() + + quantum_system.apply_CNOT10_gate(1) + quantum_system.print_state() + quantum_system.print_all_gates_applied() + + quantum_system.apply_CNOT10_gate(0) + quantum_system.print_state() + quantum_system.print_all_gates_applied() + + quantum_system.apply_H_gate(0) + quantum_system.print_state() + quantum_system.print_all_gates_applied() + + quantum_system.apply_CNOT_gate(0) + quantum_system.print_state() + quantum_system.print_all_gates_applied() + + quantum_system.apply_S_gate(0) + quantum_system.print_state() + quantum_system.print_all_gates_applied() + + quantum_system.apply_CNOT_gate(1) + quantum_system.print_state() + quantum_system.print_all_gates_applied() + + + target_qubit = 0 + control_qubit = [1] + gate_name = "H" + starting_qubit = 0 + # np.min([target_qubit,np.min(control_qubit)]) + control_gate_name = f"Controlled-{gate_name}_Cq{control_qubit}_Tq{target_qubit}" + gate = quantum_system.control_gate(control_qubits = control_qubit, target_qubit = target_qubit, gate_matrix = gates_map[gate_name][0], name=control_gate_name) + quantum_system.apply_gate(gate,starting_qubit = starting_qubit) + quantum_system.print_state() + + + target_qubit = 0 + control_qubit = 2 + swap_gate_name = f"Swap-{gate_name}_Cq{control_qubit}_Tq{target_qubit}" + gate = quantum_system.swap_n_gate(control_qubit = control_qubit, target_qubit = target_qubit, name=swap_gate_name) + quantum_system.apply_gate(gate) + quantum_system.print_state() + + + quantum_system.apply_H_gate(1) + quantum_system.print_state() + quantum_system.print_all_gates_applied() + if __name__ == "__main__": - #test_init() - #test_initialize_state() - #test_basic_gates() - #test_import_export_gate() - #test_custom_gate() + test_init() + test_initialize_state() + test_basic_gates() + test_import_export_gate() + test_custom_gate() + test_lab3_circuit() test_import_export_circuit_custom() test_import_export_circuit_basic() #test_noise() diff --git a/convert_circuit.py b/convert_circuit.py new file mode 100644 index 0000000..e2f52eb --- /dev/null +++ b/convert_circuit.py @@ -0,0 +1,111 @@ +from NQubitSystem import NQubitSystem +from pytket import Circuit, OpType +from pytket.circuit import Unitary1qBox, Unitary2qBox +import numpy as np +import re +from qiskit import QuantumCircuit +from pytket.extensions.qiskit import tk_to_qiskit +from pytket.extensions.cirq import tk_to_cirq +import cirq + +common_gates = ["X", "Y", "Z", "H", "S", "T", "CNOT", "CH", + "CY", "CZ", "CT", "CS", "SWAP", "CNOT10", "TOFFOLI"] + + +def apply_gate_tket(tket_circuit, gate_name, qubits_affected): + if gate_name == "X": + tket_circuit.X(qubits_affected[0]) + elif gate_name == "Y": + tket_circuit.Y(qubits_affected[0]) + elif gate_name == "Z": + tket_circuit.Z(qubits_affected[0]) + elif gate_name == "H": + tket_circuit.H(qubits_affected[0]) + elif gate_name == "S": + tket_circuit.S(qubits_affected[0]) + elif gate_name == "T": + tket_circuit.T(qubits_affected[0]) + elif gate_name == "CNOT": + tket_circuit.CX(qubits_affected[0], qubits_affected[1]) + elif gate_name == "CH": + tket_circuit.CH(qubits_affected[0], qubits_affected[1]) + elif gate_name == "CY": + tket_circuit.CY(qubits_affected[0], qubits_affected[1]) + elif gate_name == "CZ": + tket_circuit.CZ(qubits_affected[0], qubits_affected[1]) + elif gate_name == "CT": + angle = np.pi / 4 + tket_circuit.add_gate(OpType.CRz, angle, [ + qubits_affected[0], qubits_affected[1]]) + elif gate_name == "CS": + tket_circuit.CS(qubits_affected[0], qubits_affected[1]) + elif gate_name == "SWAP": + tket_circuit.SWAP(qubits_affected[0], qubits_affected[1]) + elif gate_name == "CNOT10": + # CNOT with control and target swapped + tket_circuit.CX(qubits_affected[1], qubits_affected[0]) + elif gate_name == "TOFFOLI": + tket_circuit.CCX(qubits_affected[0], + qubits_affected[1], qubits_affected[2]) + + +def apply_custom_gate(tket_circuit, custom_gate, qubits_affected): + if len(qubits_affected) == 1: + custom_1q_gate = Unitary1qBox(custom_gate) + tket_circuit.add_gate(custom_1q_gate, qubits_affected) + elif len(qubits_affected) == 2: + custom_2q_gate = Unitary2qBox(custom_gate) + tket_circuit.add_gate(custom_2q_gate, qubits_affected) + + +def apply_controlled_gate(tket_circuit, gate_string): + # Parse the gate_string + match = re.match(r"Controlled-(\w+)_Cq(\d+)_Tq(\d+)", gate_string) + if not match: + raise ValueError("Invalid gate string format.") + + gate, control_qubit, target_qubit = match.groups() + + # Convert qubit indices to integers + control_qubit = int(control_qubit) + target_qubit = int(target_qubit) + + # Apply the controlled gate to the circuit + if gate in common_gates and gate != "T": + # Dynamically call the gate method + getattr(tket_circuit, f'C{gate}')(control_qubit, target_qubit) + elif gate == "T": + angle = np.pi / 4 + tket_circuit.add_gate(OpType.CRz, angle, [ + control_qubit, target_qubit]) + else: + raise ValueError(f"Unsupported gate '{gate}'.") + + +def convert_to_tket(IATA_circuit): + n = IATA_circuit.n_qubits + tket_circuit = Circuit(n) + dummy_index = IATA_circuit.index + for i in range(0, n, 1): + if dummy_index % 2 == 1: + tket_circuit.X(i) + dummy_index = dummy_index/2 + for gate_applied in IATA_circuit.gates_applied: + idx, gate_name, qubits_affected, single_gate, system_gate = gate_applied + if (gate_name in common_gates): + apply_gate_tket(tket_circuit, gate_name, qubits_affected) + elif gate_name[:10] == "Controlled": + apply_controlled_gate(tket_circuit, gate_name) + else: + apply_custom_gate(tket_circuit, single_gate, qubits_affected) + return tket_circuit + + +def convert_to_qiskit(IATA_circuit): + tket_circuit = convert_to_tket(IATA_circuit) + return tk_to_qiskit(tket_circuit) + + +def convert_to_cirq(IATA_circuit): + tket_circuit = convert_to_tket(IATA_circuit) + return tk_to_cirq(tket_circuit) diff --git a/density_matrix_test.py b/density_matrix_test.py new file mode 100644 index 0000000..b1c8a99 --- /dev/null +++ b/density_matrix_test.py @@ -0,0 +1,8 @@ +from NQubitSystem import NQubitSystem + +quantum_system = NQubitSystem(n_qubits=3) +quantum_system.initialize_state([0, 1, 1]) +quantum_system.apply_H_gate(0) +quantum_system.print_state() +quantum_system.plot_density_matrix() +print(quantum_system.calculate_density_matrix()) diff --git a/test_convert.py b/test_convert.py new file mode 100644 index 0000000..7e15958 --- /dev/null +++ b/test_convert.py @@ -0,0 +1,15 @@ +from NQubitSystem import NQubitSystem +from convert_circuit import convert_to_qiskit +from convert_circuit import convert_to_tket +from convert_circuit import convert_to_cirq +from qiskit import QuantumCircuit +from qiskit.visualization import circuit_drawer +import cirq +json_file = "tests/circuit_basic.json" +IATA_circuit = NQubitSystem.import_circuit(json_file) +tket_circuit = convert_to_tket(IATA_circuit) +print(tket_circuit) +qiskit_circuit = convert_to_qiskit(IATA_circuit) +print(qiskit_circuit) +cirq_circuit = convert_to_cirq(IATA_circuit) +print(cirq_circuit)