Example #1
0
    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
Example #2
0
    def read_import(self, path):
        """
        Recursively read the imports from the module at the given path
        """

        # TODO: error/warning/diagnostics

        if path in self.path2ast:
            return self.path2ast[path]

        # TODO: this doesn't do anything graceful if the file
        # can't be opened, or doesn't exist, or anything else goes
        # wrong.  We just assume that Python will raise an exception
        # that includes a useful error message.  FIXME we should
        # be more proactive about making sure that the user
        # gets the info necessary to diagnose the problem.
        #

        try:
            fin = open(path, 'r')
            text = fin.read()
            fin.close()
        except BaseException as exc:
            NodeError.fatal_msg(None,
                                'cannot open [%s]: %s' % (path, str(exc)))
            return None

        try:
            return self.read_import_str(text, path)
        except BaseException as exc:
            NodeError.fatal_msg(
                None, 'failed to import [%s]: %s %s' % (path, type(exc), exc))
            return None
Example #3
0
    def visit_For(self, node):
        """
        Discover loop variables.

        TODO: this is incomplete; we just assume that loop variables
        are all classical.  We don't attempt to infer anything about the
        iterator.
        """

        for subnode in ast.walk(node.target):
            if isinstance(subnode, ast.Attribute):
                # This is a fatal error and we don't want to confuse
                # ourselves by trying to process the ast.Name
                # nodes beneath
                #
                name_text = pyqgl2.importer.collapse_name(subnode)
                NodeError.fatal_msg(subnode,
                                    ('loop var [%s] is not local' % name_text))

            elif isinstance(subnode, ast.Name):
                name = subnode.id

                # Warn the user if they're doing something that's
                # likely to provoke an error
                #
                if self.name_is_in_lscope(name):
                    NodeError.warning_msg(
                        subnode,
                        ('loop var [%s] hides sym in outer scope' % name))

                DebugMsg.log('FOR (%s)' % name)
                self.add_type_binding(subnode, name, QGL2.CLASSICAL)

        self.visit_body(node.body)
        self.visit_body(node.orelse)
Example #4
0
    def read_import_str(self, text, path='<stdin>', module_name='__main__'):

        ptree = ast.parse(text, mode='exec')

        self.path2ast[path] = ptree

        # label each node with the name of the input file;
        # this will make error messages that reference these
        # notes much more readable
        #
        for node in ast.walk(ptree):
            node.qgl_fname = path
            node.qgl_modname = module_name

        # The preprocessor will ignore any imports that are not
        # at the "top level" (imports that happen conditionally,
        # or when a function is executed for the first time, etc)
        # because it can't figure out if/when these imports would
        # occur, and it only understands imports that occur before
        # the execution of any other statements of the program.
        #
        # Therefore warn the programmer that any such detected
        # imports will be ignored.
        #
        # TODO: we don't make any attempt to find calls to
        # __import__() or importlib.import_module().  The
        # preprocessor always ignores these, without warning.
        #
        for node in ast.walk(ptree):
            if ((isinstance(node, ast.Import)
                 or isinstance(node, ast.ImportFrom))
                    and (node.col_offset != 0)):
                NodeError.warning_msg(
                    node,
                    ('conditional/runtime import [%s] ignored by pyqgl2' %
                     pyqgl2.ast_util.ast2str(node).strip()))

        # Populate the namespace
        #
        namespace = NameSpace(path, ptree=ptree)
        self.path2namespace[path] = namespace

        for stmnt in ptree.body:
            if isinstance(stmnt, ast.FunctionDef):
                self.add_function(namespace, stmnt.name, stmnt)
            elif isinstance(stmnt, ast.Import):
                # print('NN ADDING import %s' % ast.dump(stmnt))
                self.add_import_as(namespace, stmnt)
            elif isinstance(stmnt, ast.ImportFrom):
                # print('NN ADDING import-from %s' % ast.dump(stmnt))
                self.add_from_as(namespace, stmnt.module, stmnt)
            # We're not doing module-level variables right now; no globals
            """
            elif isinstance(stmnt, ast.Assign):
                print('NN ASSIGN %s' % ast.dump(stmnt))
            """

        # print('NN NAMESPACE %s' % str(self.path2namespace))

        return self.path2ast[path]
Example #5
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

        target = node.targets[0]

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

        value = node.value
        name = target.id

        # TODO: what if the lval is an array or dict expression?
        # Need to sort out what's referenced by the lval.

        if isinstance(value, ast.Str):
            print('CP looks like a str assignment %s' % ast.dump(node))
        elif isinstance(value, ast.Num):
            print('CP looks like a num assignment %s' % ast.dump(node))
        elif isinstance(value, ast.List):
            print('CP looks like a list assignment %s' % ast.dump(node))

        elif isinstance(value, ast.Call):
            print('CP looks like a call assignment %s' % ast.dump(node))

        self.generic_visit(node)
Example #6
0
def single_sequence(node, func_name, importer, setup=None):
    """
    Create a function that encapsulates the QGL code (for a single
    sequence) from the given AST node, which is presumed to already
    be fully pre-processed.

    TODO: we don't test that the node is fully pre-processed.
    TODO: each step of the preprocessor should mark the nodes
    so that we know whether or not they've been processed.
    """

    builder = SingleSequence(importer)

    if builder.find_sequence(node) and builder.find_imports(node):
        code = builder.emit_function(func_name, setup=setup)

        NodeError.diag_msg(node,
                           'generated code:\n#start\n%s\n#end code' % code)

        # TODO: we might want to pass in elements of the local scope
        scratch_scope = dict()
        eval(compile(code, '<none>', mode='exec'), globals(), scratch_scope)

        return scratch_scope[func_name]
    else:
        NodeError.fatal_msg(node,
                            'find_sequence failed: not a single sequence')
        return None
Example #7
0
    def add_local_var(self, name, ptree):
        if not self.check_dups(name, 'local-variable'):
            NodeError.warning_msg(ptree,
                                  'redefinition of variable [%s]' % name)
        else:
            self.order_added.append(('V', ptree))

        self.local_vars[name] = ptree
Example #8
0
    def add_local_func(self, name, ptree):
        if not self.check_dups(name, 'local-function'):
            NodeError.warning_msg(ptree,
                                  'redefinition of function [%s]' % name)
        else:
            self.order_added.append(('D', ptree))

        self.local_defs[name] = ptree
Example #9
0
    def visit_ExceptHandler(self, node):
        name = node.name
        if self.name_is_in_lscope(name):
            NodeError.warn_msg(
                node, ('exception var [%s] hides sym in outer scope' % name))

            # assume all exceptions are classical
            self.add_type_binding(subnode, subnode.id, QGL2.CLASSICAL)
        pass
Example #10
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
Example #11
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
Example #12
0
    def do_concur(self, body):

        for stmnt in body:
            if not is_seq(stmnt):
                # TODO: this error message is not helpful
                NodeError.fatal_msg(stmnt, 'expected a "with seq" block')
                return
            else:
                # TODO: The grouper should annotate the seq statement
                # so we don't have to find the qbits again.
                #
                qbits = find_all_channels(stmnt)
                if not qbits:
                    print('XXN body\n%s' % ast2str(stmnt))
                self.do_seq(qbits, stmnt.body)
Example #13
0
    def add_import_as(self, namespace, stmnt):

        namespace.native_import(pyqgl2.ast_util.ast2str(stmnt), stmnt)

        namespace.add_import_as_stmnt(stmnt)

        for imp in stmnt.names:
            subpath = resolve_path(imp.name)
            if not subpath:
                NodeError.warning_msg(stmnt,
                                      'path to [%s] not found' % imp.name)
            elif is_system_file(subpath):
                continue
            else:
                namespace.add_import_as(imp.name, imp.asname)
                self.read_import(subpath)
Example #14
0
    def do_lval(self, lval):
        assigned_names, _dotted, _arrays = self.name_finder.find_names(lval)

        for name in assigned_names:
            if name not in self.local_names:
                self.local_names[name] = self.nesting_depth
            elif self.local_names[name] > self.nesting_depth:
                # we permit the nest depth to decrease, but
                # not increase
                #
                self.local_names[name] = self.nesting_depth
            elif self.local_names[name] == CheckScoping.BUILTIN_SCOPE:
                # If the symbol is a builtin, then warn that
                # it's being reassigned
                #
                NodeError.warning_msg(
                    lval, 'reassignment of a builtin symbol [%s]' % name)
Example #15
0
def collapse_name(node):
    """
    Given the AST for a symbol reference, collapse it back into
    the original reference string

    Example, instead of the AST Attribute(Name(id='x'), addr='y')
    return 'x.y'
    """

    if isinstance(node, ast.Name):
        return node.id
    elif isinstance(node, ast.Attribute):
        return collapse_name(node.value) + '.' + node.attr
    else:
        # TODO: handle this more gracefully
        NodeError.warning_msg(
            node, 'unexpected failure to resolve [%s]' % ast.dump(node))
        return None
Example #16
0
    def visit_With(self, node):
        """
        If the node is a "with concur", then add a sequence
        for each "with seq" block in its body, with a WAIT
        preamble and SYNC(?) postamble.

        All other kinds of "with" blocks cause an error.
        """

        if is_concur(node):
            self.do_concur(node.body)

            # TODO: if there's an orelse, or anything like
            # that, then gripe here.  We can't handle that yet.

        else:
            # TODO: this error message is not helpful
            NodeError.fatal_msg(node, 'Unexpected with block')
Example #17
0
    def visit_FunctionDef(self, node):
        """
        The usual entry point: insert the names used by the
        formal parameters, and then process the body
        """

        for arg in node.args.args:
            name = arg.arg
            if name not in self.local_names:
                self.local_names[name] = CheckScoping.PARAM_SCOPE
            elif self.local_names[name] == CheckScoping.MODULE_SCOPE:
                NodeError.warning_msg(
                    node, 'formal parameter masks a module symbol [%s]' % name)
            else:
                NodeError.warning_msg(
                    node,
                    'formal parameter masks a builtin symbol [%s]' % name)

        self.do_body(node.body)
Example #18
0
def get_sequence_function(node,
                          func_name,
                          importer,
                          allocated_qregs,
                          intermediate_fout=None,
                          saveOutput=False,
                          filename=None,
                          setup=None):
    """
    Create a function that encapsulates the QGL code
    from the given AST node, which is presumed to already
    be fully pre-processed.

    TODO: we don't test that the node is fully pre-processed.
    TODO: each step of the preprocessor should mark the nodes
    so that we know whether or not they've been processed.
    """

    builder = SequenceExtractor(importer, allocated_qregs)

    builder.find_sequences(node)
    builder.find_imports(node)
    code = builder.emit_function(func_name, setup)
    if intermediate_fout:
        print(('#start function\n%s\n#end function' % code),
              file=intermediate_fout,
              flush=True)
    if saveOutput and filename:
        newf = os.path.abspath(filename[:-3] + "qgl1.py")
        with open(newf, 'w') as compiledFile:
            compiledFile.write(code)
        print("Saved compiled code to %s" % newf)

    NodeError.diag_msg(node, 'generated code:\n#start\n%s\n#end code' % code)

    # TODO: we might want to pass in elements of the local scope
    scratch_scope = dict()
    eval(compile(code, '<none>', mode='exec'), globals(), scratch_scope)
    NodeError.halt_on_error()

    return scratch_scope[func_name]
Example #19
0
    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
Example #20
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
Example #21
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
Example #22
0
    def visit_Name(self, node):
        """
        Process an ast.Name node for a symbol reference
        (not a symbol assignment, which should be done
        in do_lval).

        If we find a name that doesn't have a binding in
        the current scope, or that was defined at a higher
        nesting level than we're currently in, then warn
        the user that this might be an error.  (these aren't
        always errors, but they're strange enough that they're
        worth calling out)
        """

        name = node.id

        if name not in self.local_names:
            NodeError.warning_msg(node,
                                  'potentially undefined symbol [%s]' % name)
        elif self.local_names[name] > self.nesting_depth:
            NodeError.warning_msg(
                node, 'symbol [%s] referenced outside defining block' % name)
Example #23
0
    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])
Example #24
0
    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
Example #25
0
    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
Example #26
0
    def visit_With(self, node):
        """
        TODO: this is incomplete; we just assume that with-as variables
        are all classical.  We don't attempt to infer anything about their
        type.  (This is likely to be true in most cases, however)
        """

        for item in node.items:
            if not item.optional_vars:
                continue

            for subnode in ast.walk(item.optional_vars):
                if isinstance(subnode, ast.Attribute):
                    # This is a fatal error and we don't want to confuse
                    # ourselves by trying to process the ast.Name
                    # nodes beneath
                    #
                    name_text = pyqgl2.importer.collapse_name(subnode)
                    NodeError.fatal_msg(
                        subnode, ('with-as var [%s] is not local' % name_text))

                elif isinstance(subnode, ast.Name):
                    name = subnode.id

                    DebugMsg.log('GOT WITH (%s)' % name)

                    # Warn the user if they're doing something that's
                    # likely to provoke an error
                    #
                    if self.name_is_in_lscope(name):
                        NodeError.warn_msg(
                            subnode,
                            ('with-as var [%s] hides sym in outer scope' %
                             name))
                    self.add_type_binding(subnode, subnode.id, QGL2.CLASSICAL)

        self.visit_body(node.body)
Example #27
0
def find_sys_path_prefix():
    """
    Find the prefix of the path to the "system" libraries,
    which we want to exclude from importing and searching
    for QGL stuff.

    Where these are located depends on where Python was
    installed on the local system (and which version of
    Python, etc).  The heuristic we use is to search through
    the include path for 'ast' and assume that the path
    we has the prefix we want to omit.

    This is a hack.  In Python3, modules can be loaded
    directly out of zip files, in which case they don't
    have a "file".  We use 'ast' because it typically does,
    but there's no guarantee that this will work in
    all cases.
    """

    global SYS_PATH_PREFIX

    if SYS_PATH_PREFIX:
        return SYS_PATH_PREFIX

    try:
        path = inspect.getfile(ast)
    except TypeError as exc:
        NodeError.fatal_msg(None, 'cannot find path to system modules')

    relpath = os.path.relpath(path)

    path_prefix = relpath.rpartition(os.sep)[0]

    SYS_PATH_PREFIX = path_prefix

    return path_prefix
Example #28
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
Example #29
0
    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)
Example #30
0
    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)