def _collapse_ancillas_Z(self, rho, ancillas_pos, projections): # Measure the ancillas in the X basis in parallel in each node. # NOTE: All ancillas are collapsed in parallel, Hadamard operations # are used to measure on X basis self.check["measurement"] += 1 time = self.time_lookup["measurement"] self.check["time"] += time self.check["time1"] += time # Apply environmental error rho = errs.env_error_all(rho, 0, self.a1, time) N_ancillas = len(ancillas_pos) if N_ancillas == 1: N = len(rho.dims[0]) p_success = ops.p_measurement_single_Zbasis( rho, projections[0], N, ancillas_pos[0]) else: p_success = 1 # Collapse the qubits in parrallel # Sort list to be able to reduce dimension and keep track of positions ancillas_pos = sorted(ancillas_pos) for i in range(N_ancillas): pos = ancillas_pos[i] - i rho = self._collapse_single(rho, pos, projections[i], "Z") return p_success, rho
def _collapse_ancillas_X(self, rho, ancillas_pos, projections): # Measure the ancillas in the X basis in parallel in each node. # NOTE: All ancillas are collapsed in parallel, Hadamard operations # are used to measure on X basis self.check["measurement"] += 1 time = self.time_lookup["measurement"] self.check["time"] += time self.check["time1"] += time # Apply environmental error rho = errs.env_error_all(rho, 0, self.a1, time) N_ancillas = len(ancillas_pos) N = len(rho.dims[0]) # Apply error due to the needed rotation, use I instead of real H gate identity = qt.qeye([2] * N) rho = self._apply_single_qubit_gates(rho, [identity, identity], ancillas_pos) # Collapse the qubits in parrallel # Sort list to be able to reduce dimension and keep track of positions ancillas_pos = sorted(ancillas_pos) for i in range(N_ancillas): pos = ancillas_pos[i] - i rho = self._collapse_single(rho, pos, projections[i], "X") return 1, rho
def _collapse_ancillas_EPL(self, rho, ancillas_pos): # For the special EPL case # Measure the ancillas in the Z basis in parallel in each node. # NOTE: All ancillas are collapsed in parallel, Hadamard operations # are used to measure on X basis self.check["measurement"] += 1 time = self.time_lookup["measurement"] self.check["time"] += time self.check["time1"] += time # Apply environmental error rho = errs.env_error_all(rho, 0, self.a1, time) N_ancillas = len(ancillas_pos) if N_ancillas != 2: raise ValueError("EPL ancillas collapse: Bad number of ancillas") p_success = ps.epl(rho, N=4, ancillas_pos=ancillas_pos) projections = [1, 1] # Collapse the qubits in parrallel # Sort list to be able to reduce dimension and keep track of positions ancillas_pos = sorted(ancillas_pos) for i in range(N_ancillas): pos = ancillas_pos[i] - i rho = self._collapse_single(rho, pos, projections[i], "Z") return p_success, rho
def _append_noisy_plus(self, rho): # Append noisy |+> state to the total state rho # Generate noisy plus time, plus = self.generate_noisy_plus() # Apply environmental error rho = errs.env_error_all(rho, 0, self.a1, time) # Noisy plus tate is attached at the end of the complete state rho = qt.tensor(rho, plus) return rho
def append_circuit(self, rho=None): """ Appended circuit, and depolarize accordingly. Must be self contained event Parameters ----------- rho : (densmat) density matrix involved in the circuit, can be None depending on the circuit block """ check, rho_app = self._run() time0 = check["time0"] rho = errs.env_error_all(rho, self.a0, self.a1, time0) time1 = check["time1"] rho = errs.env_error_all(rho, 0, self.a1, time1) rho = qt.tensor(rho, rho_app) return 1, check, rho
def _swap_pair(self, rho, pair): # Apply the noise due to SWAP on two states # NOTE: use only two CNOTs to perform a SWAP self.check["two_qubit_gate"] += 3 time = self.time_lookup["two_qubit_gate"] * 3 self.check["time"] += time self.check["time1"] += time rho = self._swap_noise(rho, pair[0]) rho = self._swap_noise(rho, pair[1]) rho = errs.env_error_all(rho, 0, self.a1, time) return rho
def _append_bell_pair(self, rho): # Append single click Bell pair state to the total state rho # Generate raw Bell pair to the state. time, bell = self._generate_bell_single_click() self.check["time"] += time self.check["time0"] += time # Apply environmental error rho = errs.env_error_all(rho, self.a0, self.a1, time) # Bell state is attached at the end of the complete state rho = qt.tensor(rho, bell) return rho
def add_bell_pair(self, rho): """Append a single click Bell pair to the state. Parameters ---------- rho : (densmat) density matrix in which the bell pair appends. """ self._reset_check() time, bell = self._generate_bell_single_click() # Apply environmental error rho = errs.env_error_all(rho, self.a0, self.a1, time) self.check["time"] += time self.check["time1"] += time # Bell state is attached at the end of the complete state rho = qt.tensor(rho, bell) return 1, self.check, rho
def _apply_single_qubit_gates(self, rho, gates, operation_qubits): # Apply single qubit gates on the entire state rho N = len(rho.dims[0]) # NOTE: Number of gates is 1 because gates are applied in parallel # in the nodes self.check["single_qubit_gate"] += 1 time = self.time_lookup["single_qubit_gate"] self.check["time"] += time self.check["time1"] += time # Apply environmental error rho = errs.env_error_all(rho, 0, self.a1, time) for i in range(len(gates)): rho = errs.single_qubit_gate(rho, gates[i], self.ps, N, operation_qubits[i]) return rho
def _apply_two_qubit_gates(self, rho, controls, targets, sigma): # Apply one local two qubit Control type of gates in parallel # on each node. N = len(rho.dims[0]) # NOTE: Number of gates is 1 because gates are applied in parallel self.check["two_qubit_gate"] += 1 time = self.time_lookup["two_qubit_gate"] self.check["time"] += time self.check["time1"] += time # Apply environmental error rho = errs.env_error_all(rho, 0, self.a1, time) gates = self._get_two_qubit_gates(N, controls, targets, sigma) for i in range(len(gates)): rho = errs.two_qubit_gate(rho, gates[i], self.pg, N, controls[i], targets[i]) return rho
def _measure_random_ancillas_Z(self, rho, ancillas_pos): # NOTE: All ancillas are collapsed in parallel self.check["measurement"] += 1 time = self.time_lookup["measurement"] self.check["time"] += time self.check["time1"] += time # Apply environmental error rho = errs.env_error_all(rho, 0, self.a1, time) # Collapse the qubits in parrallel # Sort list to be able to reduce dimension and keep track of positions ancillas_pos = sorted(ancillas_pos) measurements = [] for i in range(len(ancillas_pos)): pos = ancillas_pos[i] - i m, rho = self._measure_random_single(rho, pos, "Z") measurements += [m] return measurements, rho
def _append_epl(self, rho): # Append a Bell pair generated using the EPL protocol to the # total state rho # Copy a backup of the check dictS check_backup = self.check.copy() # Get EPl and number of attempts p_success, _, rho_epl = self.start_epl() attempts = self._success_number_of_attempts(p_success) + 1 # Multiply check elements for k in self.check: self.check[k] *= (attempts) time = self.check["time0"] self.check += check_backup # Dephase the rest of the state and join them rho = errs.env_error_all(rho, self.a0, self.a1, time) rho = qt.tensor(rho, rho_epl) return rho
def run_parallel(self, rho=None, parallel=2): """ Run circuit three times in parallel, tensoring the resulting states, and dephasing the one that was generated first accordingly. Cicuits must be self contained events to be able to run in parallel. Parameters ----------- rho : (densmat) density matrix involved in the circuit, can be None depending on the circuit block parallel : (int) number of executions in parallel of the circuit """ # Get all the states, operations checks = [] rhos = [] times = [] for i in range(parallel): c, r = self._run() checks += [c] rhos += [r] times += [c["time"]] # Get max time and find its index time_max = max(times) max_index = np.where(times == time_max)[0][0] # Get state and check of max rho = rhos[max_index] check = checks[max_index] # Remove the max from the lists del times[max_index] del check[max_index] del rhos[max_index] for i in range(parallel-1): rho_app = errs.env_error_all(rhos[i], 0, self.a1, np.abs(times[i] - time_max)) rho = qt.tensor(rho, rho_app) return 1, check, rho
def start_epl(self, rho=None): """ Circuit that generates a Bell pair using the EPL protocol. NOTE: rho is not taken from arguments, only exists due to how 'circuit.py' is constructed. """ self._reset_check() # Generate Bell pairs # First pair with swap time1, bell1 = self._generate_bell_single_click() bell1 = self._swap_pair(bell1, [0, 1]) # Second pair time2, bell2 = self._generate_bell_single_click() self.check["time"] += time1 + time2 self.check["time0"] += time1 + time2 # Apply cutoff here # Dephase pair 1 rho = errs.env_error_all(bell1, self.a0, self.a1, time2) # Join state rho = qt.tensor(rho, bell2) # NOTE: Decide which state collapses. # Apply two qubit gates controls = [0, 1] targets = [2, 3] rho = self._apply_two_qubit_gates(rho, controls, targets, "X") # Measure ancillas in Z basis projections = [1] * 2 p_success, rho = self._collapse_ancillas_EPL(rho, targets) # Apply X to rotate state rho = self._apply_single_qubit_gates(rho, [qt.rx(np.pi, 2, 0)], [0]) return p_success, self.check, rho
def _swap_noise(self, rho, pos): # Apply the noise induced by the one way SWAP gate # NOTE: use only two CNOTs to perform a SWAP # Swap noise is only single qubit gate because one of the states # because a one way Swap gate is used ancilla = qt.basis(2, 0) * qt.basis(2, 0).dag() ancilla = errs.env_error_all(ancilla, self.a0, self.a1, self.check["time0"]) rho = qt.tensor(rho, ancilla) N = len(rho.dims[0]) # Define ideal gates CNOT1 = qt.cnot(N, N - 1, pos) CNOT2 = qt.cnot(N, pos, N - 1) # Apply 3 CNOTS to get a swap rho = errs.two_qubit_gate(rho, CNOT1, self.pg, N, pos, N - 1) rho = errs.two_qubit_gate(rho, CNOT2, self.pg, N, pos, N - 1) rho = errs.two_qubit_gate(rho, CNOT1, self.pg, N, pos, N - 1) # Measure the ancilla to reduce the dimension rho = errs.measure_single_Zbasis_forced(rho, self.pm, 0, N, pos) return rho