Implements a QAM-like transition.

        This function assumes ``program`` and ``program_counter`` instance variables are set
        appropriately, and that the wavefunction simulator and classical memory ``ram`` instance
        variables are in the desired QAM input state.

        :return: whether the QAM should halt after this transition.

def transition(self):
        """
        Implements a QAM-like transition.

        This function assumes ``program`` and ``program_counter`` instance variables are set
        appropriately, and that the wavefunction simulator and classical memory ``ram`` instance
        variables are in the desired QAM input state.

        :return: whether the QAM should halt after this transition.
        """
        instruction = self.program[self.program_counter]

        if isinstance(instruction, Gate):
            if instruction.name in self.defined_gates:
                self.wf_simulator.do_gate_matrix(matrix=self.defined_gates[instruction.name],
                                                 qubits=[q.index for q in instruction.qubits])
            else:
                self.wf_simulator.do_gate(gate=instruction)

            for noise_type, noise_prob in self.post_gate_noise_probabilities.items():
                self.wf_simulator.do_post_gate_noise(noise_type, noise_prob,
                                                     qubits=[q.index for q in instruction.qubits])

            self.program_counter += 1

        elif isinstance(instruction, Measurement):
            measured_val = self.wf_simulator.do_measurement(qubit=instruction.qubit.index)
            x = instruction.classical_reg  # type: MemoryReference
            self.ram[x.name][x.offset] = measured_val
            self.program_counter += 1

        elif isinstance(instruction, Declare):
            if instruction.shared_region is not None:
                raise NotImplementedError("SHARING is not (yet) implemented.")

            self.ram[instruction.name] = np.zeros(instruction.memory_size,
                                                  dtype=QUIL_TO_NUMPY_DTYPE[
                                                      instruction.memory_type])
            self.program_counter += 1

        elif isinstance(instruction, Pragma):
            # TODO: more stringent checks for what's being pragma'd and warnings
            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
            x = instruction.condition  # type: MemoryReference
            cond = self.ram[x.name][x.offset]
            if not isinstance(cond, (bool, np.bool, np.int8)):
                raise ValueError("{} requires a data type of BIT; not {}"
                                 .format(instruction.op, type(cond)))
            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 = instruction.target  # type:MemoryReference
            old = self.ram[target.name][target.offset]
            if isinstance(instruction, ClassicalNeg):
                if not isinstance(old, (int, float, np.int, np.float)):
                    raise ValueError("NEG requires a data type of REAL or INTEGER; not {}"
                                     .format(type(old)))
                self.ram[target.name][target.offset] *= -1
            elif isinstance(instruction, ClassicalNot):
                if not isinstance(old, (bool, np.bool)):
                    raise ValueError("NOT requires a data type of BIT; not {}"
                                     .format(type(old)))
                self.ram[target.name][target.offset] = not old
            else:
                raise TypeError("Invalid UnaryClassicalInstruction")

            self.program_counter += 1

        elif isinstance(instruction, (LogicalBinaryOp, ArithmeticBinaryOp, ClassicalMove)):
            left_ind = instruction.left  # type: MemoryReference
            left_val = self.ram[left_ind.name][left_ind.offset]
            if isinstance(instruction.right, MemoryReference):
                right_ind = instruction.right  # type: MemoryReference
                right_val = self.ram[right_ind.name][right_ind.offset]
            else:
                right_val = instruction.right

            if isinstance(instruction, ClassicalAnd):
                new_val = left_val & right_val
            elif isinstance(instruction, ClassicalInclusiveOr):
                new_val = left_val | right_val
            elif isinstance(instruction, ClassicalExclusiveOr):
                new_val = left_val ^ right_val
            elif isinstance(instruction, ClassicalAdd):
                new_val = left_val + right_val
            elif isinstance(instruction, ClassicalSub):
                new_val = left_val - right_val
            elif isinstance(instruction, ClassicalMul):
                new_val = left_val * right_val
            elif isinstance(instruction, ClassicalDiv):
                new_val = left_val / right_val
            elif isinstance(instruction, ClassicalMove):
                new_val = right_val
            else:
                raise ValueError("Unknown BinaryOp {}".format(type(instruction)))
            self.ram[left_ind.name][left_ind.offset] = new_val
            self.program_counter += 1

        elif isinstance(instruction, ClassicalExchange):
            left_ind = instruction.left  # type: MemoryReference
            right_ind = instruction.right  # type: MemoryReference

            tmp = self.ram[left_ind.name][left_ind.offset]
            self.ram[left_ind.name][left_ind.offset] = self.ram[right_ind.name][right_ind.offset]
            self.ram[right_ind.name][right_ind.offset] = tmp
            self.program_counter += 1

        elif isinstance(instruction, Reset):
            self.wf_simulator.reset()
            self.program_counter += 1

        elif isinstance(instruction, ResetQubit):
            # TODO
            raise NotImplementedError("Need to implement in wf simulator")
            self.program_counter += 1

        elif isinstance(instruction, Wait):
            warnings.warn("WAIT does nothing for a noiseless simulator")
            self.program_counter += 1

        elif isinstance(instruction, Nop):
            # well that was easy
            self.program_counter += 1

        elif isinstance(instruction, DefGate):
            if instruction.parameters is not None and len(instruction.parameters) > 0:
                raise NotImplementedError("PyQVM does not support parameterized DEFGATEs")
            self.defined_gates[instruction.name] = instruction.name
            self.program_counter += 1

        elif isinstance(instruction, RawInstr):
            raise NotImplementedError("PyQVM does not support raw instructions. "
                                      "Parse your program")

        elif isinstance(instruction, Halt):
            return True
        else:
            raise ValueError("Unsupported instruction type: {}".format(instruction))

        # return HALTED (i.e. program_counter is end of program)
        return self.program_counter == len(self.program)