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]]
Example #4
0
    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)))
Example #5
0
    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, :]