def _decompose_CnU(cmd): # pylint: disable=invalid-name """ Decompose a multi-controlled gate U with n control qubits into a single- controlled U. It uses (n-1) work qubits and 2 * (n-1) Toffoli gates for general U and (n-2) work qubits and 2n - 3 Toffoli gates if U is an X-gate. """ eng = cmd.engine qubits = cmd.qubits ctrl_qureg = cmd.control_qubits gate = cmd.gate n_controls = get_control_count(cmd) # specialized for X-gate if gate == XGate() and n_controls > 2: n_controls -= 1 ancilla_qureg = eng.allocate_qureg(n_controls - 1) with Compute(eng): Toffoli | (ctrl_qureg[0], ctrl_qureg[1], ancilla_qureg[0]) for ctrl_index in range(2, n_controls): Toffoli | ( ctrl_qureg[ctrl_index], ancilla_qureg[ctrl_index - 2], ancilla_qureg[ctrl_index - 1], ) ctrls = [ancilla_qureg[-1]] # specialized for X-gate if gate == XGate() and get_control_count(cmd) > 2: ctrls += [ctrl_qureg[-1]] with Control(eng, ctrls): gate | qubits Uncompute(eng)
def logical_circuit(self, eng, register, pauli_op): r""" Apply the circuit for the logical operator. The Y-logical operator does not have phase information, in order words it is exactly Y = XZ. Parameters ---------- eng : BasicEngine register : list pauli_op : str String of the format "Xi", "Yi", "Zi", where i is a integer from 0 to k-1. E.g. 'X0' will apply the 0th logical X-operator. There are a total of k logical X operators and k logical Z operators. Notes ----- - The engine will be flushed at the end. Examples -------- >> eng = ProjectQ engine... >> register = List of qubits in the engine. Apply the logical X operator on the first qubit. >> code.logical_circuit(eng, register, "X0") Apply the logical Z operator on the second qubit. >> code.logical_circuit(eng, register, "Z1"0 """ assert len(pauli_op) == 2, "'pauli_op' should be length two." assert pauli_op[0] in [ 'X', 'Y', 'Z' ], "First character of 'pauli_op' should be X, Y or Z." assert 0 <= int( pauli_op[1] ) < self.k, "Second character should be integer from 0 to k-1." if pauli_op[0] == 'X': logical_x_ith = self.logical_x[int(pauli_op[1])] pauli_str = StabilizerCode.binary_rep_to_pauli_str(logical_x_ith) elif pauli_op[0] == "Z": logical_z_ith = self.logical_z[int(pauli_op[1])] pauli_str = StabilizerCode.binary_rep_to_pauli_str(logical_z_ith) elif pauli_op[0] == 'Y': # The pauli Y is just ZX, which in binary representation is below. logical_y_ith = (self.logical_z[int(pauli_op[1])] + self.logical_x[int(pauli_op[1])]) % 2 pauli_str = StabilizerCode.binary_rep_to_pauli_str(logical_y_ith) for i, pauli_op in enumerate(pauli_str): if pauli_op == "X": XGate() | register[i] elif pauli_op == "Y": QubitOperator('Y' + str(i), -1.j) | register elif pauli_op == "Z": ZGate() | register[i] eng.flush()
def apply_stabilizer_circuit(self, eng, register, stabilizer): r""" Apply the stabilizer circuit to a ProjectQ Engine. Example: Applying "XYI" does X to first qubit register[0], and Y to second qubit register[1]. Parameters ---------- eng : BasicEngine The ProjectQ engine. register : list Holds the qubits. stabilizer : str or np.ndarray Either a pauli string representing one stabilier element or the binary representation of the one stabilizer element. Notes ----- - Engine is flushed after. Examples -------- >> eng = Project Q engine >> register = Qubits of Register >> apply_stabilizer_circuit(eng, register, "XXY") """ if isinstance(stabilizer, (list, np.ndarray)): # Convert to Pauli String. pauli_str = StabilizerCode.binary_rep_to_pauli_str(stabilizer) for i, pauli_op in enumerate(pauli_str): print(pauli_op) if pauli_op == "X": XGate() | register[i] elif pauli_op == "Z": ZGate() | register[i] elif pauli_op == "Y": # ZGate() | register[i] # XGate() | register[i] # YGate() | register[i] QubitOperator('Y' + str(i), 1.) | register eng.flush()
def decoding_circuit(self, eng, register, add_ancilla_bits=False, deallocate_nqubits=False): r""" Construct the decoding circuit to map the n-qubit to its k-qubit state. Specifically, suppose |D>_k is the unencoded k-qubit state and |D>_n is the encoded n-qubit state. The decoding circuit turns the n-qubit state |D>_n \otimes |0,...,0> tensored with k, ancilla qubits to the state |0,..,0> \otimes |D>_k. Parameters ---------- eng : BasicEngine ProjectQ engine. register : list List containing the qubits/register for the ProjectQ engine "eng". add_ancilla_bits : bool If True, it will add extra, ancilla k-qubit. If it is false, it is assumed that it was already added and included in 'eng' and 'register'. deallocate_nqubits : bool If True, at the end of decoding it will discard and delete the 'register' and will only have the k, ancilla qubits. Returns ------- list : If deallocate_nqubits is false, it returns the original 'register' plus the ancilla register appended towards the end. If deallocate_nqubits is True. If add_ancilla_bits is True, it returns the ancilla register that was created. If add_ancilla_bits is False, it assumes the register has the ancilla bits and returns that. Notes ----- - To construct the most optimal decoding circuit. The standard form for the stabilizer code needs to be constructed alongside the logical X operators. - The engine is flushed at the end. - Let r be the rank of the standard form for the stabilizer code. This decoding scheme is more efficient when 2k(r + 1) < nr then just reversing the encoding circuit. References ---------- - See Gaitan book "Quantum Error-Correction and Fault Tolerant Quantum Computing." """ if add_ancilla_bits: register_ancilla = eng.allocate_qureg(self.k) else: register_ancilla = register[self.n:] logical_x = self.logical_x logical_z = self.logical_z # Turn |D>|0,...,0> to |D>|D>. for i_ancilla in range(0, self.k): logical_z_ith = logical_z[i_ancilla] for j_qubit, binary_val in enumerate(logical_z_ith[self.n:]): if binary_val == 1: CNOT | (register[j_qubit], register_ancilla[i_ancilla]) # Turn |D>|D> to |0,...,0>|D> for i_ancilla in range(0, self.k): logical_x_ith = logical_x[i_ancilla] with Control(eng, register_ancilla[i_ancilla]): pauli_str = StabilizerCode.binary_rep_to_pauli_str( logical_x_ith) for j_qubit, pauli_op in enumerate(pauli_str): if pauli_op == "X": XGate() | register[j_qubit] elif pauli_op == "Y": QubitOperator('Y' + str(j_qubit), -1.j) | register elif pauli_op == "Z": ZGate() | register[j_qubit] eng.flush() if deallocate_nqubits: if add_ancilla_bits: All(Measure) | register del register else: All(Measure) | register[:self.n] register_ancilla = register[self.n:] del register[:self.n] return register_ancilla return register + register_ancilla
def encoding_circuit(self, eng, register, state): r""" Apply the encoding circuit to map the k-qubit "state" to its n-qubit state. Parameters ---------- state : list list of k-items, where each item is either 0 to 1 corresponding to the quantum state |x_1, ... , x_k>, where x_i is either zero or one. Notes ----- - To construct the most optimal encoding circuit. The standard form for the stabilizer code needs to be constructed alongside the logical X operators. References ---------- - See Gaitan book "Quantum Error-Correction and Fault Tolerant Quantum Computing." """ assert len( state ) == self.k, "State should be the number of unencoded qubits k." assert len( register ) == self.n, "Number of qubits should be number of encoded qubits n." logical_x = self.logical_x # Construct The last k qubits to become the specified attribute 'state'. for i, binary in enumerate(state): assert binary in [0, 1], "state should be all binary elements." if binary == 1: XGate() | register[self.n - self.k + i] eng.flush() # Construct Controlled Unitary operators To Model Logical X Operators, this is only needed # when the rank is less than n- k. if self.rank < self.numb_stab: for i in range(self.k - 1, -1, -1): # Go Thorough each un-encoded. # Get the ith Controlled unitary operator controlled_op = logical_x[i, self.rank:self.n - self.k] with Control(eng, register[i - self.k]): for j, binary in enumerate(controlled_op): if binary == 1: XGate() | register[self.rank + j] eng.flush() # Construct The application of stabilizer generators. # Go Through the first rank qubits, should be all initialized to zero, or go through # the first type 1 stabilizer generators. for i in range(0, self.rank): # Apply hadamard gate to every encoded qubit. HGate() | register[i] eng.flush() # Get pauli operator of normal stabilizer generator. pauli = self.binary_rep_to_pauli_str(self.normal_form[i]) # Apply controlled operators with the ith-qubit being controlled. for j, pauli_op in enumerate(pauli): with Control(eng, register[i]): if j != i: # The ith qubit is controlled. if pauli_op == 'X': XGate() | register[j] elif pauli_op == 'Y': QubitOperator('Y' + str(j), -1.j) | register elif pauli_op == 'Z': # Z Gate Acts trivially on |0000 \delta> # if j < i: ZGate() | register[j] eng.flush() eng.flush()
def single_syndrome_measurement(self, eng, register, stabilizer): r""" Get the syndrome measurement of stabilizer element. Note that if the length of register is n, then this allocates a new qubit, does the circuit, then deletes the allocated qubit. If length of register is n+1, then it uses the last qubit in the register as a measurement ancilla. Stabilizer element is recommended to be in standard normal form. Parameters ---------- eng : ProjectQ Engine The quantum circuit engine. register : ProjectQ Qureg or list of Qubit Either a quantum register or a list of qubits. stabilizer : (np.ndarray(2n,) or string) Binary representation of the stabilizer element or pauli string. Returns ------- int : The measurement corresponding to the stabilizer element. Zero means it commutes and negative one means it anti-commutes. References ---------- - See Quantum Error Correction Book By Daniel Lidar Page 72. """ numb_qubits = len(eng.active_qubits) # The additional qubit is for measurement purposes. if numb_qubits != self.n and numb_qubits != self.n + 1: raise TypeError( "Number of qubits allocated should match the number of encoded qubits n" " from (n,k) code or match n + 1, where last qubit is used as an " "ancilla.") # Allocate a new qubit for measurement, if it doesn't have it already. if numb_qubits == self.n: measure_qubit = eng.allocate_qubit() else: measure_qubit = register[-1] # Convert stabilizer element to pauli matrix and construct the circuit and measure it. pauli_str = stabilizer if isinstance(stabilizer, np.ndarray): pauli_str = self.binary_rep_to_pauli_str(np.array(stabilizer)) print(pauli_str) H | measure_qubit eng.flush() with Control(eng, measure_qubit): for i, pauli_element in enumerate(pauli_str): if pauli_element == 'X': XGate() | register[i] elif pauli_element == 'Y': # ZGate() | register[i] # XGate() | register[i] QubitOperator('Y' + str(i), -1.j) | register elif pauli_element == 'Z': ZGate() | register[i] elif pauli_element == "I": pass else: raise RuntimeError( "Pauli strings contained an unknown character.") eng.flush() H | measure_qubit eng.flush() Measure | measure_qubit eng.flush() result = int(measure_qubit) if numb_qubits == self.n: del measure_qubit return result
def bit_flip(engine, register): rando = np.random.random() # Get uniform random number from zero to one.. if rando < prob_error: # If it is less than probability of error. XGate() | register engine.flush()