def _apply_cnot(self, instruction): """ Apply a CNOT to the tableau :param instruction: pyquil abstract instruction. """ a, b = [value_get(x) for x in instruction.qubits] for i in range(2 * self.num_qubits): self.tableau[i, -1] = self._cnot_phase_update(i, a, b) self.tableau[i, b] = self.tableau[i, b] ^ self.tableau[i, a] self.tableau[i, a + self.num_qubits] = self.tableau[ i, a + self.num_qubits] ^ self.tableau[i, b + self.num_qubits]
def _apply_phase(self, instruction): """ Apply the phase gate instruction ot the tableau :param instruction: pyquil abstract instruction. """ qubit_label = [value_get(x) for x in instruction.qubits][0] for i in range(2 * self.num_qubits): self.tableau[i, -1] = self.tableau[i, -1] ^ ( self.tableau[i, qubit_label] * self.tableau[i, qubit_label + self.num_qubits]) self.tableau[i, qubit_label + self.num_qubits] = self.tableau[ i, qubit_label + self.num_qubits] ^ self.tableau[i, qubit_label]
def _apply_hadamard(self, instruction): """ Apply a hadamard gate on qubit defined in instruction :param instruction: pyquil abstract instruction. """ qubit_label = [value_get(x) for x in instruction.qubits][0] for i in range(2 * self.num_qubits): self.tableau[i, -1] = self.tableau[i, -1] ^ ( self.tableau[i, qubit_label] * self.tableau[i, qubit_label + self.num_qubits]) self.tableau[i, [qubit_label, qubit_label + self.num_qubits]] = self.tableau[ i, [qubit_label + self.num_qubits, qubit_label]]
def _transition(self, instruction): """ Implements a transition on the wf-qvm. Assumes entire Program() is already loaded into self.program as the synthesized list of Quilbase action objects. Possible types of instructions: Measurement gate in self.gate_set or self.defgates_set Jump, JumpTarget, JumpConditional Unary and Binary ClassicalInstruction :param action-like instruction: {Measurement, Instr, Jump, JumpTarget, JumpTarget, JumpConditional, UnaryClassicalInstruction, BinaryClassicalInstruction, Halt} instruction to execute. """ if isinstance(instruction, Measurement): # perform measurement and modify wf in-place t_qbit = value_get(instruction.qubit) t_cbit = value_get(instruction.classical_reg) measured_val, unitary = self.measurement(t_qbit, psi=None) self.wf = unitary.dot(self.wf) # load measured value into classical bit destination self.classical_memory[t_cbit] = measured_val self.program_counter += 1 elif isinstance(instruction, Gate): # apply Gate or DefGate unitary = tensor_gates(self.gate_set, self.defgate_set, instruction, self.num_qubits) self.wf = unitary.dot(self.wf) self.program_counter += 1 elif isinstance(instruction, Jump): # unconditional Jump; go directly to Label self.program_counter = self.find_label(instruction.target) elif isinstance(instruction, JumpTarget): # Label; pass straight over self.program_counter += 1 elif isinstance(instruction, JumpConditional): # JumpConditional; check classical reg cond = self.classical_memory[value_get(instruction.condition)] dest_index = self.find_label(instruction.target) if isinstance(instruction, JumpWhen): jump_if_cond = True elif isinstance(instruction, JumpUnless): jump_if_cond = False else: raise TypeError("Invalid JumpConditional") if not (cond ^ jump_if_cond): # jumping: set prog counter to JumpTarget self.program_counter = dest_index else: # not jumping: hop over this JumpConditional self.program_counter += 1 elif isinstance(instruction, UnaryClassicalInstruction): # UnaryClassicalInstruction; set classical reg target_ind = value_get(instruction.target) old = self.classical_memory[target_ind] if isinstance(instruction, ClassicalTrue): new = True elif isinstance(instruction, ClassicalFalse): new = False elif isinstance(instruction, ClassicalNot): new = not old else: raise TypeError("Invalid UnaryClassicalInstruction") self.classical_memory[target_ind] = new self.program_counter += 1 elif isinstance(instruction, BinaryClassicalInstruction): # BinaryClassicalInstruction; compute and set classical reg left_ind = value_get(instruction.left) left_val = self.classical_memory[left_ind] right_ind = value_get(instruction.right) right_val = self.classical_memory[right_ind] if isinstance(instruction, ClassicalAnd): # compute LEFT AND RIGHT, set RIGHT to the result self.classical_memory[right_ind] = left_val & right_val elif isinstance(instruction, ClassicalOr): # compute LEFT OR RIGHT, set RIGHT to the result self.classical_memory[right_ind] = left_val | right_val elif isinstance(instruction, ClassicalMove): # move LEFT to RIGHT self.classical_memory[right_ind] = left_val elif isinstance(instruction, ClassicalExchange): # exchange LEFT and RIGHT self.classical_memory[left_ind] = right_val self.classical_memory[right_ind] = left_val else: raise TypeError("Invalid BinaryClassicalInstruction") self.program_counter += 1 elif isinstance(instruction, Halt): # do nothing; set program_counter to end of program self.program_counter = len(self.program) else: raise TypeError("Invalid / unsupported instruction type: {}\n" "Currently supported: unary/binary classical " "instructions, control flow (if/while/jumps), " "measurements, and gates/defgates.".format( type(instruction)))
def identify_bits(self): """ Iterates through QAM program and finds number of qubits and cbits needed to run program. :return: number of qubits, number of classical bits used by program :rtype: tuple """ q_max, c_max = (-1, -1) for index, inst in enumerate(self.program): if isinstance(inst, Measurement): # instruction is measurement, acts on qbits and cbits if value_get(inst.qubit) > q_max: q_max = value_get(inst.qubit) elif value_get(inst.classical_reg) > c_max: c_max = value_get(inst.classical_reg) elif isinstance(inst, UnaryClassicalInstruction): # instruction acts on cbits if value_get(inst.target) > c_max: c_max = value_get(inst.target) elif isinstance(inst, BinaryClassicalInstruction): # instruction acts on cbits if value_get(inst.left) > c_max: c_max = value_get(inst.left) elif value_get(inst.right) > c_max: c_max = value_get(inst.right) elif isinstance(inst, Gate): if max(map(lambda x: value_get(x), inst.qubits)) > q_max: q_max = max(map(lambda x: value_get(x), inst.qubits)) q_max += 1 # 0-indexed c_max += 1 # 0-indexed q_limit = 51 if q_max > q_limit: # hardcoded qubit maximum raise RuntimeError("Too many qubits. Maximum qubit number " "supported: {}".format(q_limit)) return (q_max, c_max)
def _apply_measurement(self, instruction): """ Apply a measurement :param instruction: pyquil abstract instruction. """ t_qbit = value_get(instruction.qubit) t_cbit = value_get(instruction.classical_reg) # check if the output of the measurement is random # this is analogous to the case when the measurement operator does not # commute with at least one stabilizer if any(self.tableau[self.num_qubits:, t_qbit] == 1): # find the first `1'. xpa_idx = np.where( self.tableau[self.num_qubits:, t_qbit] == 1)[0][0] # take the first index xpa_idx += self.num_qubits # adjust so we can index into the tableau for ii in range( 2 * self.num_qubits ): # loop over each row and call rowsum(ii, xpa_idx) if ii != xpa_idx and self.tableau[ii, t_qbit] == 1: self._rowsum(ii, xpa_idx) # moving the operator into the destabilizer and then replacing with # the measurement operator self.tableau[xpa_idx - self.num_qubits, :] = self.tableau[xpa_idx, :] # this is like replacing the non-commuting element with the measurement operator self.tableau[xpa_idx, :] = np.zeros((1, 2 * self.num_qubits + 1)) self.tableau[xpa_idx, t_qbit + self.num_qubits] = 1 # perform the measurement self.tableau[xpa_idx, -1] = 1 if np.random.random() > 0.5 else 0 # set classical results to return self.classical_memory[t_cbit] = self.tableau[xpa_idx, -1] # outcome of measurement is deterministic...need to determine sign else: # augment tableau with a scratch space self.tableau = np.vstack( (self.tableau, np.zeros((1, 2 * self.num_qubits + 1), dtype=int))) for ii in range(self.num_qubits): # We need to check if R(i) anticommutes with Za...which it does if x_{ia} = 1 if self.tableau[ii, t_qbit] == 1: # referencing the destabilizers # TODO: Remove this for performance? # check something silly. Does the destabilizer anticommute with the observable? It SHOULD! tmp_vector_representing_z_qubit = np.zeros( (2 * self.num_qubits), dtype=int) tmp_vector_representing_z_qubit[t_qbit] = 1 assert symplectic_inner_product( tmp_vector_representing_z_qubit, self.tableau[ii, :-1]) == 1 # row sum on the stabilizers (summing up operators such that we get operator Z_{a}) # note: A-G says 2 n + 1...this is correct...but they start counting at 1 not zero self._rowsum(2 * self.num_qubits, ii + self.num_qubits) # set the classical bit to be the last element of the scratch row self.classical_memory[t_cbit] = self.tableau[-1, -1] # remove the scratch space self.tableau = self.tableau[:2 * self.num_qubits, :]