Esempio n. 1
0
def with_qiter_eq(stmnt1, stmnt2):
    """
    Given two "with-qiter" statements, determine whether they are
    identical and therefore the effect of the two statements
    can be implemented by repeating one of the statements twice.

    Returns True if the statements are identical, False otherwise.

    Note that the statements are assumed to be consecutive,
    NOT separated by a third statement.  If the statements are
    separated, then the comparison is still legitimate, but
    does not imply that the two statements can be replaced with
    iteration.

    Also note that the comparison is assumed to be done
    AFTER the evaluator has finished transforming the statements,
    including rebinding any variables and/or variable names.
    At this point, only a small subset of valid Python statements
    and expressions may appear in the given statements.  We use
    this assumption liberally, and therefore this method may
    need to change if/when the evaluator changes.
    """

    try:
        stmnt_txt1 = ast2str(stmnt1).strip()
    except BaseException as exc:
        print('with_qiter_eq ast2str failed stmnt1 %s' % str(exc))
        return False

    try:
        stmnt_txt2 = ast2str(stmnt2).strip()
    except BaseException as exc:
        print('with_qiter_eq ast2str failed stmnt2 %s' % str(exc))
        return False

    # Just to be doubly certain, try converting the text back
    # to AST again, and then check those results.

    try:
        stmnt1_ast_again = expr2ast(stmnt_txt1)
        stmnt1_txt_again = ast2str(stmnt1_ast_again).strip()
    except BaseException as exc:
        print('with_qiter_eq expr2ast failed stmnt1 %s' % str(exc))
        return False

    try:
        stmnt2_ast_again = expr2ast(stmnt_txt1)
        stmnt2_txt_again = ast2str(stmnt2_ast_again).strip()
    except BaseException as exc:
        print('with_qiter_eq expr2ast failed stmnt2 %s' % str(exc))
        return False

    if stmnt_txt1 != stmnt_txt2:
        return False
    elif stmnt1_txt_again != stmnt2_txt_again:
        return False
    else:
        return True
Esempio n. 2
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
Esempio n. 3
0
    def __init__(self, node):

        # The set all all channels observed in the input AST
        #
        self.all_channels = find_all_channels(node)

        self.blank_barrier_ast = expr2ast('Barrier()')
Esempio n. 4
0
    def make_barrier_ast(qbits, node, name='seq', bid=None):

        if not bid:
            bid = BarrierIdentifier.next_bid()

        arg_names = sorted(list(qbits))
        arg_list = '[%s]' % ', '.join(arg_names)
        barrier_name = '%s_%d' % (name, bid)

        barrier_ast = expr2ast('Barrier(\'%s\', %s)' %
                               (barrier_name, arg_list))

        # TODO: make sure that this gets all of the qbits.
        # It might need local scope info as well, which we don't
        # have here.
        #
        MarkReferencedQbits.marker(barrier_ast)

        copy_all_loc(barrier_ast, node, recurse=True)

        # Add an "implicit import" for the Barrier function
        #
        barrier_ast.value.qgl_implicit_import = ('Barrier', 'qgl2.qgl1control',
                                                 'Barrier')

        # print('MARKED %s [%s] %s' %
        #         (barrier_name,
        #             str(barrier_ast.qgl2_referenced_qbits), str(qbits)))

        return barrier_ast
Esempio n. 5
0
    def make_ugoto_call(self, label):
        """
        Create an unconditional goto call
        """

        # Instead of constructing the AST piece by piece,
        # construct a string containing the code we
        # want, and then parse that to create the AST.
        #
        goto_str = 'Goto(BlockLabel(\'%s\'))' % label
        goto_ast = expr2ast(goto_str)
        return goto_ast
Esempio n. 6
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])
Esempio n. 7
0
def make_qrepeat(iter_cnt, body, subname=''):
    """
    Create a with-Qrepeat node with the given iter_cnt and body

    The subname string may be used to create variants on the
    basic with-Qrepeat node, to deal with embedded repeats
    (i.e. when part of a for loop can be rewritten as a Qrepeat,
    but the rest cannot).
    """

    repeat_txt = 'with Qrepeat%s(%d):\n    pass' % (subname, iter_cnt)
    repeat_ast = expr2ast(repeat_txt)
    copy_all_loc(repeat_ast, body[0], recurse=True)

    repeat_ast.body = body

    return repeat_ast
Esempio n. 8
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
Esempio n. 9
0
    def group(node, local_vars=None):

        # We want to make a copy of params of the root node,
        # but we DO NOT need to recurse through the body of
        # the node (and this is potentially a time-sink).
        # So we grab a reference to the node body, reassign
        # the node body to the empty list, make a copy of
        # the node recursively, and then put the original
        # body back.
        #
        orig_body = node.body
        node.body = list()
        new_node = quickcopy(node)
        node.body = orig_body

        all_qbits = MarkReferencedQbits.marker(node,
                                               local_vars=local_vars,
                                               force_recursion=True)

        # TODO: need to check that it's a FunctionDef

        alloc_stmnts = list()
        body_stmnts = list()

        # Divide between qbit allocation and ordinary
        # statements.  This is ugly: it would be better
        # to move qbit allocation outside qgl2main. TODO
        #
        for stmnt in node.body:
            # if (isinstance(stmnt, ast.Assign) and
            #         stmnt.targets[0].qgl_is_qbit):
            if isinstance(stmnt, ast.Assign):
                alloc_stmnts.append(stmnt)
            else:
                body_stmnts.append(stmnt)

        new_groups = list()

        qbits = sorted(all_qbits)

        for i in range(len(qbits)):
            qbit = qbits[i]

            # An optimization: we need to make a copy of body_stmnts
            # so we can modify it, but for the last iteration, we can
            # cannibalize it and use the input list as our scratch_body.
            # In the (common) case that there's only one qbit, this
            # has a large effect on performance.
            #
            if i == (len(qbits) - 1):
                scratch_body = body_stmnts
            else:
                scratch_body = quickcopy(body_stmnts)

            pruned_body = QbitPruner(set([qbit])).prune_body(scratch_body)
            if not pruned_body:
                continue

            with_group = expr2ast('with group(%s): pass' % qbit)
            copy_all_loc(with_group, node, recurse=True)

            # Insert a special Barrier named "group_marker..."
            # at the start of each per-qbit group.
            # The compiler later looks for this to know
            # which sequence is for which Qubit.
            bid = BarrierIdentifier.next_bid()
            beg_barrier = AddSequential.make_barrier_ast([qbit],
                                                         with_group,
                                                         name='group_marker',
                                                         bid=bid)
            if DebugMsg.ACTIVE_LEVEL < 3:
                print("For qbit %s, inserting group_marker: %s" %
                      (qbit, ast2str(beg_barrier)))

            with_group.body = [beg_barrier] + pruned_body

            with_group.qbit = qbit
            MarkReferencedQbits.marker(with_group, local_vars=local_vars)

            new_groups.append(with_group)

        with_grouped = expr2ast('with grouped: pass')
        copy_all_loc(with_grouped, node, recurse=True)
        MarkReferencedQbits.marker(with_grouped, local_vars=local_vars)

        with_grouped.body = new_groups

        new_node.body = alloc_stmnts + list([with_grouped])

        return new_node