コード例 #1
0
ファイル: flatten.py プロジェクト: stjordanis/pyqgl2
    def visit_Assign(self, node):
        """
        Flatten an assignment. The only assignments we should see
        are Qubits, measurements, and runtime computations. If we see a
        measurement, we need to schedule the pulse (the RHS). A runtime
        computation is passed through as an opaque STORE command.
        """
        if not isinstance(node.value, ast.Call):
            NodeError.error_msg(node,
                                "Unexpected assignment [%s]" % ast2str(node))
        if hasattr(node, 'qgl2_type'):
            if node.qgl2_type == 'qbit':
                return node
            elif node.qgl2_type == 'measurement':
                new_node = ast.Expr(value=node.value)
                new_node.qgl2_type = 'measurement'
                pyqgl2.ast_util.copy_all_loc(new_node, node)
                return new_node
            elif node.qgl2_type == 'runtime_call':
                # put the runtime_call in a STORE command
                new_node = expr2ast('Store()')
                # first argument is the STORE destination
                # TODO we want to re-write the target as an address
                target_str = ast2str(node.targets[0]).strip()
                new_node.value.args.append(ast.Str(s=target_str))
                # second argument is the str() representation
                # of the runtime call
                call_str = ast2str(node.value).strip()
                new_node.value.args.append(ast.Str(s=call_str))
                new_node.qgl2_type = 'runtime_call'
                pyqgl2.ast_util.copy_all_loc(new_node, node, recurse=True)
                return new_node

        return node
コード例 #2
0
    def add_type_binding(self, node, name, name_type):
        """
        Add a binding between a name and a type, in the local context.

        Gripe and do nothing if there is already a binding for this
        name in either the parameter or local scope, and it disagrees
        with the requested binding.

        The node parameter is used only to generate error messages
        that can be traced back to the original code, since the node
        contains the file and line number of the code prior to
        any transformation
        """

        if name in self.parameter_names:
            old_type = self.parameter_names[name]
            if old_type != name_type:
                NodeError.error_msg(node, ('parameter type changed %s -> %s' %
                                           (old_type, name_type)))
        elif name in self.local_names:
            old_type = self.local_names[name]
            if old_type != name_type:
                NodeError.error_msg(
                    node, 'type changed %s -> %s' % (old_type, name_type))
        else:
            NodeError.diag_msg(node, 'add type %s -> %s' % (name, name_type))
            self.local_names[name] = name_type
コード例 #3
0
    def find_imports(self, node):
        '''Fill in self.stub_imports with all the per module imports needed'''

        default_namespace = node.qgl_fname

        for subnode in ast.walk(node):
            if (isinstance(subnode, ast.Call)
                    and isinstance(subnode.func, ast.Name)):
                funcname = subnode.func.id

                # QRegister calls will be stripped by find_sequences, so skip
                if funcname == 'QRegister':
                    continue

                # If we created a node without an qgl_fname,
                # then use the default namespace instead.
                # FIXME: This is a hack, but it will work for now.
                #
                if not hasattr(subnode, 'qgl_fname'):
                    namespace = default_namespace
                else:
                    namespace = subnode.qgl_fname

                if hasattr(subnode, 'qgl_implicit_import'):
                    (sym_name, module_name, orig_name) = \
                            subnode.qgl_implicit_import
                else:
                    fdef = self.importer.resolve_sym(namespace, funcname)

                    if not fdef:
                        NodeError.error_msg(
                            subnode,
                            'cannot find import info for [%s]' % funcname)
                        return False
                    elif not fdef.qgl_stub_import:
                        NodeError.error_msg(subnode,
                                            'not a stub: [%s]' % funcname)
                        return False

                    (sym_name, module_name, orig_name) = fdef.qgl_stub_import

                if orig_name:
                    import_str = '%s as %s' % (orig_name, sym_name)
                else:
                    import_str = sym_name

                if module_name not in self.stub_imports:
                    self.stub_imports[module_name] = set()

                self.stub_imports[module_name].add(import_str)

        return True
コード例 #4
0
    def check_conflicts(self, node):

        all_seen = set()

        for refs in self.qbit_sets.values():
            if not refs.isdisjoint(all_seen):
                conflict = refs.intersection(all_seen)
                NodeError.error_msg(
                    node, '%s appear in multiple concurrent statements' %
                    str(', '.join(list(conflict))))

            all_seen.update(refs)

        return all_seen
コード例 #5
0
    def find_sequences(self, node):
        '''
        Input AST node is the main function definition.
        Strips out QRegister creation statements and builds
        a list of corresponding Qubit creation statements.
        Converts calls on QRegisters into calls on Qubits.'''

        if not isinstance(node, ast.FunctionDef):
            NodeError.fatal_msg(node, 'not a function definition')
            return False

        self.qbits = self.qbits_from_qregs(self.allocated_qregs)
        for q in self.qbits:
            # Using QubitFactory here means the qubit must already exist
            # If did Channels.Qubit, we'd be creating it new; but it wouldn't be in the ChannelLibrary
            stmnt = ast.parse("QBIT_{0} = QubitFactory('q{0}')".format(q))
            self.qbit_creates.append(stmnt)

        lineNo = -1
        while lineNo + 1 < len(node.body):
            lineNo += 1
            # print("Line %d of %d" % (lineNo+1, len(node.body)))
            stmnt = node.body[lineNo]
            # print("Looking at stmnt %s" % stmnt)
            if is_qbit_create(stmnt):
                # drop it
                continue

            elif isinstance(stmnt, ast.Expr):
                # expand calls on QRegisters into calls on Qubits
                if (hasattr(stmnt, 'qgl2_type')
                        and (stmnt.qgl2_type == 'stub'
                             or stmnt.qgl2_type == 'measurement')):
                    new_stmnts = self.expand_qreg_call(stmnt)
                    self.sequence.extend(new_stmnts)
                else:
                    self.sequence.append(stmnt)
            else:
                NodeError.error_msg(stmnt,
                                    'orphan statement %s' % ast.dump(stmnt))

        # print("Seqs: %s" % self.sequences)
        if not self.sequence:
            NodeError.warning_msg(node, "No qubit operations discovered")
            return False

        return True
コード例 #6
0
ファイル: single.py プロジェクト: stjordanis/pyqgl2
    def find_sequence(self, node):

        if not isinstance(node, ast.FunctionDef):
            NodeError.fatal_msg(node, 'not a function definition')
            return False

        self.qbits = find_all_channels(node)

        if len(self.qbits) == 0:
            NodeError.error_msg(node, 'no channels found')
            return False
        else:
            NodeError.diag_msg(node, "Found channels %s" % self.qbits)

        lineNo = -1
        while lineNo + 1 < len(node.body):
            lineNo += 1
            # print("Line %d of %d" % (lineNo+1, len(node.body)))
            stmnt = node.body[lineNo]
            # print("Looking at stmnt %s" % stmnt)
            assignment = self.is_qbit_create(stmnt)
            if assignment:
                self.qbit_creates.append(assignment)
                continue
            elif is_concur(stmnt):
                # print("Found concur at line %d: %s" % (lineNo+1,stmnt))
                for s in stmnt.body:
                    if is_seq(s):
                        # print("Found with seq for qbits %s: %s" % (s.qgl_chan_list, ast2str(s)))
                        #print("With seq next at line %d: %s" % (lineNo+1,s))
                        if str(s.qgl_chan_list) not in self.sequences:
                            self.sequences[str(s.qgl_chan_list)] = list()
                        thisSeq = self.sequences[str(s.qgl_chan_list)]
                        # print("Append body %s" % s.body)
                        # for s2 in s.body:
                        #     print(ast2str(s2))
                        thisSeq += s.body
                        #print("lineNo now %d" % lineNo)
                    else:
                        NodeError.error_msg(
                            s, "Not seq next at line %d: %s" % (lineNo + 1, s))
            elif isinstance(stmnt, ast.Expr):
                if len(self.qbits) == 1:
                    # print("Append expr %s to sequence for %s" % (ast2str(stmnt), self.qbits))
                    if len(self.sequences) == 0:
                        self.sequences[list(self.qbits)[0]] = list()
                    self.sequences[list(self.qbits)[0]].append(stmnt)
                else:
                    NodeError.error_msg(
                        stmnt, 'orphan statement %s' % ast.dump(stmnt))
            else:
                NodeError.error_msg(stmnt,
                                    'orphan statement %s' % ast.dump(stmnt))
        # print("Seqs: %s" % self.sequences)
        return True
コード例 #7
0
ファイル: importer.py プロジェクト: stjordanis/pyqgl2
    def find_stub_import(self, decnode, funcname):
        """
        Find the import info encoded in a stub declaration

        TODO: doesn't do anything useful with errors/bad input
        """

        if not isinstance(decnode, ast.Call):
            NodeError.fatal_msg(
                decnode,
                'bad use of find_stub_import [%s]' % ast.dump(decnode))

        args = decnode.args
        n_args = len(args)

        from_name = None
        orig_name = None

        if n_args == 0:
            # TODO: should complain
            pass

        if n_args > 0:
            if not isinstance(args[0], ast.Str):
                NodeError.error_msg(
                    decnode,
                    'qgl2stub arg[0] must be str [%s]' % ast.dump(args[0]))
            else:
                from_name = args[0].s

        if n_args > 1:
            if not isinstance(args[1], ast.Str):
                NodeError.error_msg(
                    decnode,
                    'qgl2stub arg[1] must be str [%s]' % ast.dump(args[1]))
            else:
                orig_name = args[1].s

        if n_args > 2:
            # TODO: should complain
            pass

        return (funcname, from_name, orig_name)
コード例 #8
0
 def expand_arg(self, arg):
     '''
     Expands a single argument to a stub call. QRegisters are expanded
     to a list of constituent Qubits. QRegister subscripts are similar,
     except that the slice selects which Qubits to return. So, given
         a = QRegister(2)
         b = QRegister(1)
     we do (in AST shorthand):
         expand_arg("a") -> ["QBIT_1", "QBIT_2"]
         expand_arg("a[1]") -> ["QBIT_2"]
     '''
     expanded_args = []
     if isinstance(arg, ast.Name) and arg.id in self.allocated_qregs:
         qreg = self.allocated_qregs[arg.id]
         # add an argument for each constituent qubit in the QRegister
         for n in range(len(qreg)):
             new_arg = ast.Name(id=qreg.use_name(n), ctx=ast.Load())
             expanded_args.append(new_arg)
     elif (isinstance(arg, ast.Subscript)
           and arg.value.id in self.allocated_qregs):
         # add an argument for the subset of qubits referred to
         # by the QReference
         # eval the subscript to extract the slice
         qreg = self.allocated_qregs[arg.value.id]
         try:
             qref = eval(ast2str(arg), None, self.allocated_qregs)
         except:
             NodeError.error_msg(
                 arg, "Error evaluating QReference [%s]" % ast2str(arg))
         # convert the slice into a list of indices
         idx = range(len(qreg))[qref.idx]
         if not hasattr(idx, '__iter__'):
             idx = (idx, )
         for n in idx:
             new_arg = ast.Name(id=qreg.use_name(n), ctx=ast.Load())
             expanded_args.append(new_arg)
     else:
         # don't expand it
         expanded_args.append(arg)
     return expanded_args
コード例 #9
0
ファイル: single.py プロジェクト: stjordanis/pyqgl2
    def find_imports(self, node):

        default_namespace = node.qgl_fname

        for subnode in ast.walk(node):
            if (isinstance(subnode, ast.Call)
                    and isinstance(subnode.func, ast.Name)):
                funcname = subnode.func.id

                # If we created a node without an qgl_fname,
                # then use the default namespace instead.
                # FIXME: This is a hack, but it will work for now.
                #
                if not hasattr(subnode, 'qgl_fname'):
                    namespace = default_namespace
                else:
                    namespace = subnode.qgl_fname

                fdef = self.importer.resolve_sym(namespace, funcname)
                if not fdef:
                    print('ERROR %s funcname' % funcname)
                    NodeError.error_msg(
                        subnode, 'cannot find import info for [%s]' % funcname)
                elif not fdef.qgl_stub_import:
                    NodeError.error_msg(subnode, 'not a stub: [%s]' % funcname)
                else:
                    # print('FI AST %s' % ast.dump(fdef))
                    (sym_name, module_name, orig_name) = fdef.qgl_stub_import

                    if orig_name:
                        import_str = '%s as %s' % (orig_name, sym_name)
                    else:
                        import_str = sym_name

                    if module_name not in self.stub_imports:
                        self.stub_imports[module_name] = set()

                    self.stub_imports[module_name].add(import_str)

        return True
コード例 #10
0
ファイル: importer.py プロジェクト: stjordanis/pyqgl2
    def native_load(self, node=None):
        """
        Exec the entire text of the file, so that the native_globals
        will be properly initialized
        """

        try:
            fin = open(self.path, 'r')
            text = fin.read()
            fin.close()
        except BaseException as exc:
            NodeError.error_msg(
                None, 'read of [%s] failed: %s' % (self.path, str(exc)))
            return False

        try:
            exec(text, self.native_globals)
            return True
        except BaseException as exc:
            NodeError.error_msg(
                None, 'import of [%s] failed: %s' % (self.path, str(exc)))
            return False
        return True
コード例 #11
0
ファイル: flatten.py プロジェクト: stjordanis/pyqgl2
    def make_cgoto_call(self, label, node, cmp_operator, cmp_addr, value):
        """
        Create a conditional goto call
        """

        if isinstance(cmp_operator, ast.Eq):
            cmp_ast = expr2ast('CmpEq("%s", %s)' % (cmp_addr, str(value)))
        elif isinstance(cmp_operator, ast.NotEq):
            cmp_ast = expr2ast('CmpNeq("%s", %s)' % (cmp_addr, str(value)))
        elif isinstance(cmp_operator, ast.Gt):
            cmp_ast = expr2ast('CmpGt("%s", %s)' % (cmp_addr, str(value)))
        elif isinstance(cmp_operator, ast.Lt):
            cmp_ast = expr2ast('CmpLt("%s", %s)' % (cmp_addr, str(value)))
        else:
            NodeError.error_msg(
                node, 'Unallowed comparison operator [%s]' % ast2str(node))
            return None
        label_ast = expr2ast('Goto(BlockLabel(\'%s\'))' % label)

        pyqgl2.ast_util.copy_all_loc(cmp_ast, node, recurse=True)
        pyqgl2.ast_util.copy_all_loc(label_ast, node, recurse=True)

        return list([cmp_ast, label_ast])
コード例 #12
0
ファイル: importer.py プロジェクト: stjordanis/pyqgl2
    def native_import(self, text, node):
        """
        Do a "native import", updating self.native_globals
        with the results.

        This can be ugly if the import executes arbitrary code (i.e.
        prints things on the screen, or futzes with something else).

        The text must be an import statement, or sequence of
        import statements (ast2str turns a single statement with
        a list of symbol clauses into a list of statements)
        i.e. "from foo import bar as baz" or "from whatever import *"
        or "import something"

        The node is used to create meaningful diagnostic or
        error messages, and must be provided.

        Returns True if successful, False otherwise.
        """

        # A hack to avoid doing imports on "synthesized" imports
        # that don't have line numbers in the original source code
        #
        if (not node) or (not hasattr(node, 'lineno')):
            return

        try:
            exec(text, self.native_globals)
            return True
        except BaseException as exc:
            if node:
                caller_fname = node.qgl_fname
            else:
                caller_fname = '<unknown>'
            NodeError.error_msg(
                node, 'in %s [%s] failed: %s' % (caller_fname, text, str(exc)))
            return False
コード例 #13
0
ファイル: qreg.py プロジェクト: DebasishMaji/pyqgl2
    def factory(node, local_vars):
        '''
        Evaluates a ast.Call node of a QRegister and returns its value.

        local_vars is a dictionary of symbol -> value bindings
        '''
        if not is_qbit_create(node):
            NodeError.error_msg(
                node,
                "Attempted to create a QRegister from an invalid AST node [%s]."
                % ast2str(node))

        # convert args into values
        arg_values = []
        for arg in node.value.args:
            if isinstance(arg, ast.Num):
                arg_values.append(arg.n)
            elif isinstance(arg, ast.Str):
                arg_values.append(arg.s)
            elif isinstance(arg, ast.Name) and arg.id in local_vars:
                arg_values.append(local_vars[arg.id])
            elif is_qbit_subscript(arg, local_vars):
                # evaluate the subscript to extract the referenced qubits
                parent_qreg = local_vars[arg.value.id]
                try:
                    arg_val = eval(ast2str(arg), None, local_vars)
                except:
                    NodeError.error_msg(
                        node, "Unhandled qreg subscript [%s]" % ast2str(arg))
                sub_qubits = parent_qreg.qubits[arg_val.idx]
                if hasattr(sub_qubits, '__iter__'):
                    arg_values.extend("q" + str(n) for n in sub_qubits)
                else:
                    arg_values.append("q" + str(sub_qubits))
            else:
                NodeError.error_msg(
                    node,
                    "Unhandled argument to QRegister [%s]" % ast2str(arg))

        return QRegister(*arg_values)
コード例 #14
0
ファイル: importer.py プロジェクト: stjordanis/pyqgl2
    def find_type_decl(self, node):
        """
        Copied from check_qbit.

        Both need to be refactored.
        """

        q_args = list()
        q_return = None

        if node is None:
            NodeError.warning_msg(node, 'unexpected None node')
            return None

        if not isinstance(node, ast.FunctionDef):
            NodeError.warning_msg(
                node, 'expected a FunctionDef, got [%s]' % ast.dump(node))
            return None

        if node.returns:
            ret = node.returns

            if isinstance(ret, ast.Name):
                if ret.id == QGL2.QBIT:
                    q_return = QGL2.QBIT
                elif ret.id == QGL2.CLASSICAL:
                    q_return = QGL2.CLASSICAL
#                elif ret.id == QGL2.QBIT_LIST:
#                    q_return = QGL2.QBIT_LIST
                elif ret.id == QGL2.PULSE:
                    q_return = QGL2.PULSE
                elif ret.id == QGL2.CONTROL:
                    q_return = QGL2.CONTROL
                elif ret.id == QGL2.SEQUENCE:
                    q_return = QGL2.SEQUENCE
                else:
                    NodeError.error_msg(
                        node, 'unsupported return type [%s]' % ret.id)

        # FIXME: What about kwonlyargs? or storing the defaults?
        if node.args.args:
            for arg in node.args.args:
                # print('>> %s' % ast.dump(arg))

                name = arg.arg
                annotation = arg.annotation
                if not annotation:
                    q_args.append('%s:%s' % (name, QGL2.CLASSICAL))
                elif isinstance(annotation, ast.Name):
                    if annotation.id == QGL2.QBIT:
                        q_args.append('%s:%s' % (name, QGL2.QBIT))
                    elif annotation.id == QGL2.CLASSICAL:
                        q_args.append('%s:%s' % (name, QGL2.CLASSICAL))


#                    elif annotation.id == QGL2.QBIT_LIST:
#                        q_args.append('%s:%s' % (name, QGL2.QBIT_LIST))
                    elif annotation.id == QGL2.PULSE:
                        q_args.append('%s:%s' % (name, QGL2.PULSE))
                    elif annotation.id == QGL2.CONTROL:
                        q_args.append('%s:%s' % (name, QGL2.CONTROL))
                    elif annotation.id == QGL2.SEQUENCE:
                        q_args.append('%s:%s' % (name, QGL2.SEQUENCE))
                    else:
                        NodeError.error_msg(
                            node, ('unsupported parameter annotation [%s]' %
                                   annotation.id))
                else:
                    NodeError.error_msg(
                        node, 'unsupported parameter annotation [%s]' %
                        ast.dump(annotation))

        # print('NN NAME %s (%s) -> %s' %
        #         (node.name, str(q_args), str(q_return)))

        return (q_args, q_return)
コード例 #15
0
ファイル: importer.py プロジェクト: stjordanis/pyqgl2
    def native_eval(self, expr, local_variables=None, mode='eval'):
        """
        Evaluate the given expr, which may be an expression or a
        statement represented by an AST node or a text string.
        If mode is 'eval', then the expr must be an expression,
        but if it is 'exec' then it may be a statement.

        If local_variables is not None, it is assumed to reference
        a dictionary containing local bindings.  It should NOT
        be a reference to the global bindings (either for this
        namespace, or any other global bindings).

        Returns (success, value), where success indicates whether
        the evaluation succeeded or failed, and value is the value
        of the expression.  The process of evaluation the expression
        may also modify bindings in local_variables,

        Note that if the evaluation of the expr raises an
        exception, this exception will be caught and the result
        will be treated as failure (even if the intent of the
        expression was to raise an exception).  QGL2 doesn't
        understand exceptions.

        NOTE: this evaluation is not safe, and may damage the
        environment of the caller.  There is no safeguard against
        this right now.
        """

        if (not isinstance(expr, str)) and (not isinstance(expr, ast.AST)):
            print('INVALID EXPR type %s' % str(type(expr)))
            return False, None

        # If we get AST, then there are many variations on what
        # we could get (it could look like an Expr, or an Expression,
        # or a Module, etc.  By converting the AST to a text string,
        # this removes all of the ambiguity and lets us parse the
        # program again, in the local context.
        #
        # This is inefficient for the computer (to keep going back and
        # forth between text and parse trees) but efficient for the
        # implementer.
        #
        if isinstance(expr, ast.AST):
            expr_str = pyqgl2.ast_util.ast2str(expr)
        else:
            expr_str = expr

        try:
            final_expr = compile(expr_str, '<nofile>', mode=mode)
        except SyntaxError as exc:
            print('Syntax error in native_eval: %s' % str(exc))
            return False, None
        except BaseException as exc:
            print('Error in native_eval: %s' % str(exc))
            return False, None

        try:
            if local_variables is None:
                local_variables = dict()

            # global_variables = dict.copy(self.native_globals)

            # print('EXPR %s' % expr_str.strip())
            val = eval(final_expr, self.native_globals, local_variables)
            return True, val
        except BaseException as exc:
            # If the expr was AST and came from the preprocessor,
            # try to format the error message accordingly
            #
            # Otherwise just attempt to print something meaningful
            #
            if isinstance(expr, ast.AST) and hasattr(expr, 'qgl_fname'):
                NodeError.error_msg(
                    expr, ('ast eval failure [%s]: type %s %s' %
                           (expr_str.strip(), str(type(exc)), str(exc))))
            else:
                print('eval failure [%s]: %s' % (expr_str.strip(), str(exc)))
            return False, None
コード例 #16
0
ファイル: importer.py プロジェクト: stjordanis/pyqgl2
    def __init__(self, path, qglmain_name=None, text=None):

        # map from path to AST
        #
        self.path2ast = dict()

        # map from path to NameSpace
        #
        self.path2namespace = dict()

        # The error/warning messages are clearer if we always
        # use the relpath
        #
        if not path or path == '<stdin>':
            self.base_fname = '<stdin>'
        else:
            self.base_fname = os.path.relpath(path)

        # Reference to the main function; initially None because
        # we haven't read it in yet
        #
        self.qglmain = None

        if text:
            self.read_import_str(text, self.base_fname)
        else:
            self.read_import(self.base_fname)

        # TODO: if the user asks for a specific main, then go
        # back and use it.  Don't gripe if the user has already defined
        # one.  Resolve the name with respect to the namespace
        # of base_fname

        if qglmain_name:
            qglmain_def = self.resolve_sym(self.base_fname, qglmain_name)

            if not qglmain_def:
                NodeError.error_msg(
                    None, 'no definition for qglmain [%s]' % qglmain_name)
            elif not qglmain_def.qgl_func:
                NodeError.error_msg(
                    None, 'qglmain [%s] not declared QGL' % qglmain_name)
            else:
                self.qglmain = qglmain_def
                qglmain_def.qgl_main = True

        if self.qglmain:
            NodeError.diag_msg(None,
                               'using [%s] as qglmain' % self.qglmain.name)
        else:
            NodeError.warning_msg(None,
                                  'warning: no qglmain declared or chosen')

        # This is a hack to make sure that the base file
        # is read in as a "native import".  Since there isn't
        # an explicit "import" of this file anywhere, we don't
        # have an AST node that contains the code for this import.
        # We can't use None, because this importer uses this as
        # a sentinel value, so we use self.qgl2main.  This is
        # bogus -- we should make a fake node for this purpose
        # FIXME

        fin = open(self.base_fname, 'r')
        text = fin.read()
        fin.close()

        namespace = self.path2namespace[self.base_fname]
        namespace.native_import(text, self.qglmain)
コード例 #17
0
ファイル: const_prop.py プロジェクト: stjordanis/pyqgl2
    def assign_simple(self, node):

        target = node.targets[0]
        value = node.value

        print('XX qbit_scope %s %s' %
              (str(self._qbit_scope()), ast.dump(node)))
        if not isinstance(target, ast.Name):
            return node

        if target.id in self._qbit_local():
            msg = 'reassignment of qbit \'%s\' forbidden' % target.id
            self.error_msg(node, msg)
            return node

        if (target.id + ':qbit') in self._qbit_scope():
            msg = 'reassignment of qbit parameter \'%s\' forbidden' % target.id
            self.error_msg(node, msg)
            return node

        print('XX qbit_scope %s %s' %
              (str(self._qbit_scope()), ast.dump(node)))

        if isinstance(value, ast.Name):
            # print('CHECKING %s' % str(self._qbit_scope()))
            if (value.id + ':qbit') in self._qbit_scope():
                self.warning_msg(
                    node, 'aliasing qbit parameter \'%s\' as \'%s\'' %
                    (value.id, target.id))
                self._extend_local(target.id)
            elif value.id in self._qbit_local():
                self.warning_msg(
                    node, 'aliasing local qbit \'%s\' as \'%s\'' %
                    (value.id, target.id))
                self._extend_local(target.id)

        elif isinstance(value, ast.Call):
            func_name = pyqgl2.importer.collapse_name(value.func)
            func_def = self.importer.resolve_sym(value.qgl_fname, func_name)

            # If we can't find the function definition, or it's not declared
            # to be QGL, then we can't handle it.  Return immediately.
            #
            if not func_def:
                NodeError.error_msg(value,
                                    'function [%s] not defined' % func_name)
                return node

            if func_def.returns:
                rtype = func_def.returns
                if (isinstance(rtype, ast.Name) and rtype.id == QGL2.QBIT):
                    # Not sure what happens if we get here: we might
                    # have a wandering variable that we know is a qbit,
                    # but we never know which one.
                    #
                    print('XX EXTENDING LOCAL (%s)' % target.id)
                    self._extend_local(target.id)
                    target.qgl_is_qbit = True

            if not func_def.qgl_func:
                # TODO: this seems bogus.  We should be able to call
                # out to non-QGL functions
                #
                NodeError.error_msg(
                    value, 'function [%s] not declared to be QGL2' % func_name)
                return node

            print('NNN lookup [%s] got %s' % (func_name, str(func_def)))
            print('NNN FuncDef %s' % ast.dump(func_def))
            print('NNN CALL [%s]' % func_name)

            # When we're figuring out whether something is a call to
            # the Qbit assignment function, we look at the name of the
            # function as it is defined (i.e, as func_def), not as it
            # is imported (i.e., as func_name).
            #
            # This makes the assumption that ANYTHING named 'Qubit'
            # is a Qbit assignment function, which is lame and should
            # be more carefully parameterized.  Things to think about:
            # looking more deeply at its signature and making certain
            # that it looks like the 'right' function and not something
            # someone mistakenly named 'Qubit' in an unrelated context.
            #
            if isinstance(value, ast.Call) and (func_def.name
                                                == QGL2.QBIT_ALLOC):
                self._extend_local(target.id)
                print('XX EXTENDED to include %s %s' %
                      (target.id, str(self._qbit_local())))

        return node
コード例 #18
0
ファイル: flatten.py プロジェクト: stjordanis/pyqgl2
    def visit_If(self, node):
        """
        flatten an "if" statement, returning a new list of
        expressions that represent the flattened sequence
        """

        # make sure that the test involves runtime values.
        # This is the only kind of test that should survive
        # to this point; classical test would have already
        # been executed.
        # FIXME add this check
        # Also, if the test contains a call, we should
        # move the evaluation of that call to a expression before
        # the comparison

        if (isinstance(node.test, ast.Name)
                or isinstance(node.test, ast.Call)):
            mask = 0
            cmp_addr = node.test.id
            cmp_operator = ast.NotEq()
        elif (isinstance(node.test, ast.UnaryOp)
              and isinstance(node.test.op, ast.Not)):
            mask = 0
            cmp_addr = node.test.operand.id
            cmp_operator = ast.Eq()
        elif isinstance(node.test, ast.Compare):
            # FIXME the value can be on either side of the comparison
            # this assumes that it is on the right
            mask = node.test.comparators[0].n
            cmp_addr = node.test.left.id
            cmp_operator = node.test.ops[0]
        else:
            NodeError.error_msg(
                node.test,
                'unhandled test expression [%s]' % ast2str(node.test))
            return node

        if_label, end_label = LabelManager.allocate_labels('if', 'if_end')

        cond_ast = self.make_cgoto_call(if_label, node.test, cmp_operator,
                                        cmp_addr, mask)

        # cond_ast is actually a list of AST nodes
        new_body = cond_ast

        end_goto_ast = self.make_ugoto_call(end_label)
        if_ast = self.make_label_call(if_label)
        end_label_ast = self.make_label_call(end_label)

        pyqgl2.ast_util.copy_all_loc(end_goto_ast, node, recurse=True)
        pyqgl2.ast_util.copy_all_loc(if_ast, node, recurse=True)
        pyqgl2.ast_util.copy_all_loc(end_label_ast, node, recurse=True)

        if node.orelse:
            new_body += self.flatten_body(node.orelse)

        new_body.append(end_goto_ast)
        new_body.append(if_ast)
        new_body += self.flatten_body(node.body)
        new_body.append(end_label_ast)

        return new_body
コード例 #19
0
ファイル: main.py プロジェクト: DebasishMaji/pyqgl2
def compile_function(filename,
                     main_name=None,
                     toplevel_bindings=None,
                     saveOutput=False,
                     intermediate_output=None):

    NodeError.reset()

    print('\n\nCOMPILING [%s] main %s' %
          (filename, main_name if main_name else '(default)'))

    # Use whether intermediate_output is None to decide
    # whether to call printout blocks at all
    # Old code set intermediate_output to /dev/null

    if intermediate_output:
        try:
            intermediate_fout = open(intermediate_output, 'w')
        except BaseException as exc:
            NodeError.fatal_msg(None,
                                ('cannot save intermediate output in [%s]' %
                                 intermediate_output))
    else:
        intermediate_fout = None

    # Process imports in the input file, and find the main.
    # If there's no main, then bail out right away.

    try:
        rel_path = os.path.relpath(filename)
        filename = rel_path
    except Exception as e:
        # If that wasn't a good path, give up immediately
        NodeError.error_msg(
            None, "Failed to make relpath from %s: %s" % (filename, e))

    NodeError.halt_on_error()

    print('%s: CALLING IMPORTER' % datetime.now())
    importer = NameSpaces(filename, main_name)
    if not importer.qglmain:
        NodeError.fatal_msg(None, 'no qglmain function found')

    NodeError.halt_on_error()

    ptree = importer.qglmain

    if intermediate_output:
        ast_text_orig = pyqgl2.ast_util.ast2str(ptree)
        print(('%s: ORIGINAL CODE:\n%s' % (datetime.now(), ast_text_orig)),
              file=intermediate_fout,
              flush=True)

    # When QGL2 flattens various kinds of control flow and runtime
    # computations it emits QGL1 instruction that the user may not
    # have imported.
    #
    # TODO: this is a hack, but the approach of adding these
    # blindly to the namespace is also a hack.  This is a
    # placeholder until we figure out a cleaner approach.

    required_imports = [
        'Wait', 'Barrier', 'Goto', 'LoadCmp', 'CmpEq', 'CmpNeq', 'CmpGt',
        'CmpLt', 'BlockLabel', 'Store'
    ]

    modname = ptree.qgl_fname
    for symbol in required_imports:
        if not add_import_from_as(importer, modname, 'qgl2.qgl1', symbol):
            NodeError.error_msg(ptree, 'Could not import %s' % symbol)
    NodeError.halt_on_error()

    ptree1 = ptree

    # We may need to iterate over the inlining processes a few times,
    # because inlining may expose new things to inline.
    #
    # TODO: as a stopgap, we're going to limit iterations to 20, which
    # is enough to handle fairly deeply-nested, complex non-recursive
    # programs.  What we do is iterate until we converge (the outcome
    # stops changing) or we hit this limit.  We should attempt at this
    # point to prove that the expansion is divergent, but we don't
    # do this, but instead assume the worst if the program is complex
    # enough to look like it's "probably" divergent.
    #

    print('%s: CALLING INLINER' % datetime.now())
    MAX_ITERS = 20
    for iteration in range(MAX_ITERS):

        print('%s: ITERATION %d' % (datetime.now(), iteration))

        inliner = Inliner(importer)
        ptree1 = inliner.inline_function(ptree1)
        NodeError.halt_on_error()

        if intermediate_output:
            print(('INLINED CODE (iteration %d):\n%s' %
                   (iteration, pyqgl2.ast_util.ast2str(ptree1))),
                  file=intermediate_fout,
                  flush=True)

        if inliner.change_cnt == 0:
            NodeError.diag_msg(
                None, ('expansion converged after iteration %d' % iteration))
            break

    if iteration == (MAX_ITERS - 1):
        NodeError.error_msg(
            None,
            ('expansion did not converge after %d iterations' % MAX_ITERS))

    # transform passed toplevel_bindings into a local_context dictionary

    # FIXME: If the qgl2main provides a default for an arg
    # that is 'missing', then don't count it as missing

    arg_names = [x.arg for x in ptree1.args.args]
    if isinstance(toplevel_bindings, tuple):
        if len(arg_names) != len(toplevel_bindings):
            NodeError.error_msg(
                None,
                'Invalid number of arguments supplied to qgl2main (got %d, expected %d)'
                % (len(toplevel_bindings), len(arg_names)))
        local_context = {
            name: quickcopy(value)
            for name, value in zip(arg_names, toplevel_bindings)
        }
    elif isinstance(toplevel_bindings, dict):
        invalid_args = toplevel_bindings.keys() - arg_names
        if len(invalid_args) > 0:
            NodeError.error_msg(
                None, 'Invalid arguments supplied to qgl2main: {}'.format(
                    invalid_args))
        missing_args = arg_names - toplevel_bindings.keys()
        if len(missing_args) > 0:
            NodeError.error_msg(
                None,
                'Missing arguments for qgl2main: {}'.format(missing_args))
        local_context = quickcopy(toplevel_bindings)
    elif toplevel_bindings:
        NodeError.error_msg(
            None, 'Unrecognized type for toplevel_bindings: {}'.format(
                type(toplevel_bindings)))
    else:
        local_context = None
    NodeError.halt_on_error()

    evaluator = EvalTransformer(SimpleEvaluator(importer, local_context))

    print('%s: CALLING EVALUATOR' % datetime.now())
    ptree1 = evaluator.visit(ptree1)
    NodeError.halt_on_error()

    if DebugMsg.ACTIVE_LEVEL < 3:
        print('%s: EVALUATOR RESULT:\n%s' %
              (datetime.now(), pyqgl2.ast_util.ast2str(ptree1)))
    # It's very hard to read the intermediate form, before the
    # QBIT names are added, so we don't save this right now.
    # print(('EVALUATOR RESULT:\n%s' % pyqgl2.ast_util.ast2str(ptree1)),
    #         file=intermediate_fout, flush=True)

    # Dump out all the variable bindings, for debugging purposes
    #
    # print('EV total state:')
    # evaluator.print_state()

    evaluator.replace_bindings(ptree1.body)

    if DebugMsg.ACTIVE_LEVEL < 3:
        print('%s: EVALUATOR REBINDINGS:\n%s' %
              (datetime.now(), pyqgl2.ast_util.ast2str(ptree1)))
    if intermediate_output:
        print(
            ('EVALUATOR + REBINDINGS:\n%s' % pyqgl2.ast_util.ast2str(ptree1)),
            file=intermediate_fout,
            flush=True)

    # base_namespace = importer.path2namespace[filename]

    # if intermediate_output:
    #     text = base_namespace.pretty_print()
    #     print(('EXPANDED NAMESPACE:\n%s' % text),
    #           file=intermediate_fout, flush=True)

    new_ptree1 = ptree1

    # Try to flatten out repeat, range, ifs
    flattener = Flattener()
    print('%s: CALLING FLATTENER' % datetime.now())
    new_ptree2 = flattener.visit(new_ptree1)
    NodeError.halt_on_error()
    if intermediate_output:
        print(('%s: FLATTENED CODE:\n%s' %
               (datetime.now(), pyqgl2.ast_util.ast2str(new_ptree2))),
              file=intermediate_fout,
              flush=True)

    # TODO Is it ever necessary to replace bindings again at this point?
    # evaluator.replace_bindings(new_ptree2.body)
    # evaluator.get_state()

    if intermediate_output:
        print(('Final qglmain: %s\n' % new_ptree2.name),
              file=intermediate_fout,
              flush=True)

    new_ptree3 = new_ptree2

    # Done. Time to generate the QGL1

    # Try to guess the proper function name
    fname = main_name
    if not fname:
        if isinstance(ptree, ast.FunctionDef):
            fname = ptree.name
        else:
            fname = "qgl1Main"

    # Get the QGL1 function that produces the proper sequences
    print('%s: GENERATING QGL1 SEQUENCE FUNCTION' % datetime.now())
    qgl1_main = get_sequence_function(new_ptree3,
                                      fname,
                                      importer,
                                      evaluator.allocated_qbits,
                                      intermediate_fout,
                                      saveOutput,
                                      filename,
                                      setup=evaluator.setup())
    NodeError.halt_on_error()
    return qgl1_main
コード例 #20
0
    def assign_simple(self, node):

        target = node.targets[0]
        value = node.value

        DebugMsg.log('XX qbit_scope %s %s' %
                     (str(self._qbit_scope()), ast.dump(node)))

        if not isinstance(target, ast.Name):
            return node

        if target.id in self._qbit_local():
            msg = 'reassignment of qbit \'%s\' forbidden' % target.id
            self.error_msg(node, msg)
            return node

        if (target.id + ':qbit') in self._qbit_scope():
            msg = 'reassignment of qbit parameter \'%s\' forbidden' % target.id
            self.error_msg(node, msg)
            return node

        DebugMsg.log('XX qbit_scope %s %s' %
                     (str(self._qbit_scope()), ast.dump(node)))

        if isinstance(value, ast.Name):
            # print('CHECKING %s' % str(self._qbit_scope()))
            if (value.id + ':qbit') in self._qbit_scope():
                self.warning_msg(
                    node, 'aliasing qbit parameter \'%s\' as \'%s\'' %
                    (value.id, target.id))
                self._extend_local(target.id)
            elif value.id in self._qbit_local():
                self.warning_msg(
                    node, 'aliasing local qbit \'%s\' as \'%s\'' %
                    (value.id, target.id))
                self._extend_local(target.id)

        elif isinstance(value, ast.Call):
            func_name = pyqgl2.importer.collapse_name(value.func)
            func_def = self.importer.resolve_sym(value.qgl_fname, func_name)

            # If we can't find the function definition, check to see
            # whether it's a builtin.  If we can't find it, or it's
            # not declared to be QGL, then we can't check it.
            # Return immediately.
            #
            # TODO the way we check whether a function is a builtin
            # is a non-portable hack.
            #
            # The warning about the function not being defined "locally"
            # is annoying because it will occur for any function imported
            # from a module in the system libraries, because we don't
            # import these right now.  This needs a better approach.
            #
            if not func_def:
                """
                # This error is no longer valid; it's not an error
                # if it's not a builtin
                #
                if func_name not in __builtins__:
                    NodeError.error_msg(
                            value, 'function [%s] not defined' % func_name)
                """
                return node

            if func_def.returns:
                rtype = func_def.returns
                if (isinstance(rtype, ast.Name) and rtype.id == QGL2.QBIT):
                    # Not sure what happens if we get here: we might
                    # have a wandering variable that we know is a qbit,
                    # but we never know which one.
                    #
                    DebugMsg.log('XX EXTENDING LOCAL (%s)' % target.id)
                    self._extend_local(target.id)
                    target.qgl_is_qbit = True

            if not func_def.qgl_func:
                # TODO: this seems bogus.  We should be able to call
                # out to non-QGL functions
                #
                NodeError.error_msg(
                    value, 'function [%s] not declared to be QGL2' % func_name)
                return node

            DebugMsg.log('NNN lookup [%s] got %s' % (func_name, str(func_def)))
            DebugMsg.log('NNN FuncDef %s' % ast.dump(func_def))
            DebugMsg.log('NNN CALL [%s]' % func_name)

            # When we're figuring out whether something is a call to
            # the Qbit assignment function, we look at the name of the
            # function as it is defined (i.e, as func_def), not as it
            # is imported (i.e., as func_name).
            #
            # This makes the assumption that ANYTHING named 'Qubit'
            # or 'QubitFactory'
            # is a Qbit assignment function, which is lame and should
            # be more carefully parameterized.  Things to think about:
            # looking more deeply at its signature and making certain
            # that it looks like the 'right' function and not something
            # someone mistakenly named 'Qubit' in an unrelated context.
            #
            if (isinstance(value, ast.Call)
                    and (func_def.name == QGL2.QBIT_ALLOC
                         or func_def.name == QGL2.QBIT_ALLOC2)):
                self._extend_local(target.id)
                DebugMsg.log('XX EXTENDED to include %s %s' %
                             (target.id, str(self._qbit_local())))

        return node
コード例 #21
0
    def visit_Assign(self, node):

        # FIXME: can't handle nested tuples properly
        # For now we're not even going to try.

        if not isinstance(node.targets[0], ast.Name):
            NodeError.warning_msg(node, 'tuple returns not supported yet')
            self.generic_visit(node)
            return node

        target = node.targets[0]

        print('VA target0: %s' % ast.dump(target))

        value = node.value
        name = target.id

        if not isinstance(target, ast.Name):
            # should this be considered an error?
            # it's not an error in Python, but it's hard for us to handle.
            return node

        if self.is_qbit_parameter(name):
            msg = 'reassignment of qbit parameter \'%s\' forbidden' % name
            NodeError.error_msg(node, msg)
            return node

        if self.is_qbit_local(name):
            msg = 'reassignment of qbit \'%s\' forbidden' % name
            NodeError.error_msg(node, msg)
            return node

        if isinstance(value, ast.Name):
            if not self.name_is_in_lscope(value.id):
                NodeError.error_msg(node, 'unknown symbol \'%s\'' % value.id)
                return node

            if self.is_qbit_parameter(name):
                self.warning_msg(
                    node, 'aliasing qbit parameter \'%s\' as \'%s\'' %
                    (value.id, name))
                self.add_type_binding(value, name, QGL2.QBIT)
                target.qgl_is_qbit = True
            elif self.is_qbit_local(name):
                self.warning_msg(
                    node,
                    'aliasing local qbit \'%s\' as \'%s\'' % (value.id, name))
                self.add_type_binding(value, name, QGL2.QBIT)
                target.qgl_is_qbit = True
            else:
                self.add_type_binding(value, name, QGL2.CLASSICAL)
                target.qgl_is_qbit = False

        elif isinstance(value, ast.Call):

            func_name = pyqgl2.importer.collapse_name(value.func)
            func_def = self.importer.resolve_sym(value.qgl_fname, func_name)

            # FIXME: for debugging only!
            new_scope = FindTypes.find_lscope(self.importer, func_def, value,
                                              self)
            # FIXME: end debugging

            # If we can't find the function definition, or it's not declared
            # to be QGL, then we can't handle it.  Return immediately.
            #
            if not func_def:
                NodeError.warning_msg(value,
                                      'function [%s] not found' % func_name)
                self.add_type_binding(value, name, 'unknown')
                return node

            if func_def.returns:
                rtype = func_def.returns
                if (isinstance(rtype, ast.Name) and rtype.id == QGL2.QBIT):
                    # Not sure what happens if we get here: we might
                    # have a wandering variable that we know is a qbit,
                    # but we never know which one.
                    #
                    DebugMsg.log('extending local (%s)' % name)
                    self.add_type_binding(value, name, QGL2.QBIT)
                    target.qgl_is_qbit = True

            if not func_def.qgl_func:
                # TODO: this seems bogus.  We should be able to call
                # out to non-QGL functions
                #
                NodeError.error_msg(
                    value, 'function [%s] not declared to be QGL2' % func_name)
                return node

            # When we're figuring out whether something is a call to
            # the Qbit assignment function, we look at the name of the
            # function as it is defined (i.e, as func_def), not as it
            # is imported (i.e., as func_name).
            #
            # This makes the assumption that ANYTHING named 'Qubit' or 'QubitFactory'
            # is a Qbit assignment function, which is lame and should
            # be more carefully parameterized.  Things to think about:
            # looking more deeply at its signature and making certain
            # that it looks like the 'right' function and not something
            # someone mistakenly named 'Qubit' in an unrelated context.
            #
            if isinstance(value,
                          ast.Call) and (func_def.name == QGL2.QBIT_ALLOC
                                         or func_def.name == QGL2.QBIT_ALLOC2):
                self.add_type_binding(value, name, QGL2.QBIT)

        return node
コード例 #22
0
    def concur_wait(self, node):
        """
        Synchronize the start of each seq block within a concur block,

        Add seq blocks for any "missing" channels so we can
        add a Barrier instruction for each of them as well
        """
        global BARRIER_CTR

        # This method will be destructive, unless we make a new
        # copy of the AST tree first
        #
        node = deepcopy(node)

        seen_channels = set()

        # Channels in this with_concur
        concur_channels = find_all_channels(node)

        # For creating the Barriers, we want QGL1 scoped variables that will be real channel instances.
        # We basically have that already.
        real_chans = set()
        for chan in concur_channels:
            real_chans.add(chan)

        start_barrier = BARRIER_CTR
        end_barrier = start_barrier + 1
        BARRIER_CTR += 2

        for stmnt in node.body:
            if not is_seq(stmnt):
                NodeError.error_msg(stmnt,
                                    'non-seq block inside concur block?')
                return node

            seq_channels = find_all_channels(stmnt)

            if seq_channels.intersection(seen_channels):
                NodeError.error_msg(stmnt,
                                    'seq blocks have overlapping channels')
                return node

            seen_channels = seen_channels.union(seq_channels)

            chan_name = ','.join(seq_channels)

            # mark stmnt with chan_name or seq_channels in another way
            if hasattr(stmnt, 'qgl_chan_list'):
                oldChanSet = set(stmnt.qgl_chan_list)
                newChanSet = seq_channels
                oldMissing = newChanSet - oldChanSet
                oldExtra = oldChanSet - newChanSet
                if len(oldMissing) > 0:
                    NodeError.diag_msg(
                        stmnt, 'marked chan list %s was missing %s' %
                        (str(oldChanSet), str(oldMissing)))
                if len(oldExtra) > 0:
                    NodeError.diag_msg(
                        stmnt, 'marked chan list %s had extra %s' %
                        (str(oldChanSet), str(oldExtra)))
            NodeError.diag_msg(stmnt,
                               'Marking chan list %s' % (str(seq_channels)))
            stmnt.qgl_chan_list = list(seq_channels)

            new_seq_body = list()

            # Helper to ensure the string we feed to AST doesn't put quotes around
            # our Qubit variable names
            def appendChans(bString, chans):
                bString += '['
                first = True
                for chan in chans:
                    if first:
                        bString += str(chan)
                        first = False
                    else:
                        bString += "," + str(chan)
                bString += ']'
                return bString

            # Add global ctr, chanlist=concur_channels
            # FIXME: Hold concur_channels as a string? List?
            bstring = 'Barrier("%s", ' % str(start_barrier)
            bstring = appendChans(bstring, list(real_chans))
            bstring += ')\n'
            barrier_ast = expr2ast(bstring)
            # barrier_ast = expr2ast('Barrier(%s, %s)\n' % (str(start_barrier), list(real_chans)))
            copy_all_loc(barrier_ast, node)
            barrier_ast.channels = concur_channels
            # print("*****Start barrier: %s" % pyqgl2.ast_util.ast2str(barrier_ast))

            new_seq_body.append(barrier_ast)

            new_seq_body += stmnt.body

            bstring = 'Barrier("%s", ' % str(end_barrier)
            bstring = appendChans(bstring, list(real_chans))
            bstring += ')\n'
            end_barrier_ast = expr2ast(bstring)
            #end_barrier_ast = expr2ast('Barrier(%s, %s)\n' % (str(end_barrier), list(real_chans)))
            copy_all_loc(end_barrier_ast, node)
            # Add global ctr, chanlist=concur_channels
            end_barrier_ast.channels = concur_channels

            # print('End AST: %s' % ast2str(end_barrier_ast))

            new_seq_body.append(end_barrier_ast)

            stmnt.body = new_seq_body

        # FIXME: In new thinking, is the proper unseen set the global one,
        # Or only those local to this with concur. I think only local
        for unseen_chan in concur_channels - seen_channels:
            #print('DIAG %s' % ast2str(stmnt))
            NodeError.diag_msg(
                stmnt,
                'channels unreferenced in concur: %s' % str(unseen_chan))

            bstring = 'with seq:\n    Barrier("%s", ' % str(start_barrier)
            bstring = appendChans(bstring, list(real_chans))
            bstring += ')\n    Barrier("%s",' % str(end_barrier)
            bstring = appendChans(bstring, list(real_chans))
            bstring += ')\n'
            empty_seq_ast = expr2ast(bstring)
            # print('Empty AST: %s' % ast2str(empty_seq_ast))
            # empty_seq_ast = expr2ast(
            #         'with seq:\n    Barrier(%s, %s)\n    Barrier(%s, %s)' % (str(start_barrier), list(real_chans), str(end_barrier), list(real_chans)))

            # Mark empty_seq_ast with unseen_chan
            empty_seq_ast.qgl_chan_list = [unseen_chan]
            copy_all_loc(empty_seq_ast, node)
            node.body.append(empty_seq_ast)

        return node
コード例 #23
0
    def process_params(self, func_def, call=None, call_scope=None):

        # The formal parameters are an AST object.
        # The way they are represented is a little awkward;
        # all parameters (positional and keyword) are in a
        # positional list (because Python can handle keyword
        # parameters as positional parameters) and then the
        # keyword default values are in a separate positional
        # list.)

        type_bindings = dict()
        val_bindings = dict()
        all_arg_names = list()

        # First, pretend all the parameters are positional
        #
        for arg in func_def.args.args:
            arg_name = arg.arg
            arg_type = arg.annotation
            if arg_type and isinstance(arg_type, ast.Name):
                arg_type_name = arg_type.id
            else:
                arg_type_name = 'unknown'

            if arg_name in all_arg_names:
                NodeError.error_msg(
                    arg, 'repeated parameter name \'%s\'' % arg_name)


#            if arg_type_name not in [QGL2.CLASSICAL, QGL2.QBIT, 'unknown', QGL2.CONTROL, QGL2.PULSE, QGL2.SEQUENCE, QGL2.QBIT_LIST]:
            if arg_type_name not in [QGL2.CLASSICAL, QGL2.QBIT, 'unknown']:
                NodeError.warning_msg(
                    arg,
                    ('parameter type \'%s\' is not supported' % arg_type_name))

            all_arg_names.append(arg_name)

            type_bindings[arg_name] = arg_type_name
            val_bindings[arg_name] = None

        # Then process any defaults that were provided
        #
        default_vals = func_def.args.defaults
        if default_vals:
            default_names = all_arg_names[:-len(default_vals)]

            for ind in range(len(default_vals)):
                val_bindings[default_names[ind]] = default_vals[ind]

                # TODO: we need to make sure that the default
                # values actually match the declared type, if any
                #
                # NOTE that the default value is an AST, which could be
                # almost any expression.  Many expressions are going to
                # be a headache for us, so maybe we should disallow
                # many of them.

        # Now replace the default values with whatever is in the
        # actuals, if any actuals are provided.

        if call:
            seen_args = set()
            print('CALL %s' % ast.dump(call))
            if call.args:
                for ind in range(len(call.args)):
                    seen_args.add(all_arg_names[ind])
                    val_bindings[all_arg_names[ind]] = call.args[ind]

            # TODO: If there were fewer args than required, then
            # gripe. TODO: if there were unexpected arguments, gripe

            for kwarg in call.keywords:
                name = kwarg.arg

                if name in seen_args:
                    NodeError(
                        call,
                        'more than one value for parameter \'%s\'' % name)

                seen_args.add(name)
                val_bindings[name] = kwarg.value

            print('CALL %s' % str(val_bindings))

        # TODO: if provided a surrounding scope and a call, then try to
        # infer types from actual parameters.  For example, if one of
        # the actual parameters is 'x', and we know the type of 'x', then
        # propogate it.
        #
        # Right now we don't try to statically determine values.
        #
        #
        # TODO: this is incomplete

        if call and call_scope:

            # Create a dictionary of known types from the given
            # call_scope.  Note that we are only interested in
            # known types, so omit any "unknown" types
            #
            scope_types = dict()
            for name in call_scope.parameter_names:
                name_type = call_scope.parameter_names[name]
                if name_type != 'unknown':
                    scope_types[name] = name_type

            for name in call_scope.local_names:
                name_type = call_scope.local_names[name]
                if name_type != 'unknown':
                    scope_types[name] = name_type

            # Now look at each actual parameter, and try
            # to infer what type it has.  If it's a number or
            # string, it's classical.  If it's the value of
            # a variable, look in scope_types to see what we
            # know about that variable (if anything).  If it's
            # a method call, look at the definition of the
            # method to see whether it has a declared type.
            #
            for name in type_bindings:
                actual_val = val_bindings[name]

                if isinstance(actual_val, ast.Num):
                    type_bindings[name] = QGL2.CLASSICAL

                elif isinstance(actual_val, ast.Str):
                    type_bindings[name] = QGL2.CLASSICAL

                elif isinstance(actual_val, ast.NameConstant):
                    type_bindings[name] = QGL2.CLASSICAL

                elif isinstance(actual_val, ast.Name):
                    if actual_val.id in scope_types:
                        type_bindings[name] = scope_types[actual_val.id]

                elif isinstance(actual_val, ast.Call):
                    called_func_name = pyqgl2.importer.collapse_name(
                        actual_val.func)
                    called_func = self.importer.resolve_sym(
                        actual_val.qgl_fname, func_name)

                    if not called_func:
                        NodeError.warning_msg(
                            value,
                            'function [%s] not found' % called_func_name)
                        continue
                    elif called_func.returns:
                        rtype = called_func_def.returns
                        if isinstance(rtype, ast.Name):
                            rtype_name = rtype.id

                            #                            if rtype_name not in [QGL2.CLASSICAL, QGL2.QBIT, 'unknown', QGL2.SEQUENCE, QGL2.PULSE, QGL2.CONTROL, QGL2.QBIT_LIST]:
                            if rtype_name not in [
                                    QGL2.CLASSICAL, QGL2.QBIT, 'unknown'
                            ]:
                                NodeError.warning_msg(
                                    arg,
                                    ('parameter type \'%s\' is not supported' %
                                     arg_type_name))

                        type_bindings[name] = rtype_name

        return val_bindings, type_bindings