Exemplo n.º 1
0
    def canonize_column(self, j, sweep, xrange=None, **canonize_opts):
        check_opt('sweep', sweep, ('up', 'down'))

        if xrange is None:
            xrange = (0, self._Lx - 1)

        if sweep == 'down':
            for i in range(min(xrange), max(xrange), +1):
                self.canonize_between((i, j), (i + 1, j), **canonize_opts)

        else:
            for i in range(max(xrange), min(xrange), -1):
                self.canonize_between((i, j), (i - 1, j), **canonize_opts)
Exemplo n.º 2
0
    def canonize_row(self, i, sweep, yrange=None, **canonize_opts):
        check_opt('sweep', sweep, ('right', 'left'))

        if yrange is None:
            yrange = (0, self._Ly - 1)

        if sweep == 'right':
            for j in range(min(yrange), max(yrange), +1):
                self.canonize_between((i, j), (i, j + 1), **canonize_opts)

        else:
            for j in range(max(yrange), min(yrange), -1):
                self.canonize_between((i, j), (i, j - 1), **canonize_opts)
Exemplo n.º 3
0
    def compress_row(self, i, sweep, yrange=None, **compress_opts):
        check_opt('sweep', sweep, ('right', 'left'))
        compress_opts.setdefault('absorb', 'right')

        if yrange is None:
            yrange = (0, self._Ly - 1)

        if sweep == 'right':
            for j in range(min(yrange), max(yrange), +1):
                self.compress_between((i, j), (i, j + 1), **compress_opts)

        else:
            for j in range(max(yrange), min(yrange), -1):
                self.compress_between((i, j), (i, j - 1), **compress_opts)
Exemplo n.º 4
0
    def compress_column(self, j, sweep, xrange=None, **compress_opts):
        check_opt('sweep', sweep, ('up', 'down'))

        if xrange is None:
            xrange = (0, self._Lx - 1)

        if sweep == 'down':
            compress_opts.setdefault('absorb', 'right')
            for i in range(min(xrange), max(xrange), +1):
                self.compress_between((i, j), (i + 1, j), **compress_opts)

        else:
            compress_opts.setdefault('absorb', 'left')
            for i in range(max(xrange), min(xrange), -1):
                self.compress_between((i - 1, j), (i, j), **compress_opts)
Exemplo n.º 5
0
    def find_tensor_coo(self, t, get='coo'):
        '''Get the coo of the given tensor `t`.
        Important: assumes coo tag is of the form "...x,y",
        e.g. "Sx,y". 
        
        get: {'coo', 'tag', 'ind'}
            -'coo': returns tuple[int]
            -'tag': returns tag string e.g. "Sx,y"
            -'ind': returns index string e.g. "kx,y"
        '''
        check_opt("get", get, ('coo', 'tag', 'ind'))

        p = re.compile(self.site_tag_id.format('\d', '\d'))
        coo_tag, = (s for s in t.tags if p.match(s))
        coo = int(coo_tag[-3]), int(
            coo_tag[-1])  # This assumes tag is ~ "...x,y"

        return {
            'coo': coo,
            'tag': coo_tag,
            'ind': self.site_ind_id.format(*coo)
        }[get]
Exemplo n.º 6
0
    def gate(
            self,
            G,
            coos,
            contract='auto_split',
            tags=('GATE', ),
            inplace=False,
            info=None,
            **compress_opts,
    ):
        '''
        contract: {False, 'reduce_split', 'triangle_absorb', 'reduce_split_lr'}
                -False: leave gate uncontracted at sites
            [For 2-body ops:]
                -reduce_split: Absorb dummy, apply gate with `qtn.tensor_2d.reduce_split`, 
                    then reinsert dummy. (NOTE: this one seems very slow)
                -reduce_split_lr: leave dummy as-is, treat gate as a LR interaction.
                    The final bonds are much smaller this way!
            [For 3-body ops:]
                -triangle_absorb: use `three_body_op.triangle_gate_absorb` to 
                    apply the 3-body gate. Assumes `coos` is ordered like 
                    ~ (vertex, vertex, face)!
            [For any n-body:]
                -auto_split: will automatically choose depending on n.
                    n=1 -> contract = True
                    n=2 -> contract = 'reduce_split_lr'
                    n=3 -> contract = 'triangle_absorb'
        '''

        check_opt("contract", contract,
                  (False, True, 'reduce_split', 'triangle_absorb',
                   'reduce_split_lr', 'auto_split'))

        psi = self if inplace else self.copy()

        if is_lone_coo(coos):
            coos = (coos, )
        else:
            coos = tuple(coos)

        numsites = len(coos)  #num qubits acted on

        if contract == 'auto_split':
            contract = {
                1: True,
                2: 'reduce_split_lr',
                3: 'triangle_absorb'
            }[numsites]

        #inds like 'k{x},{y}'
        site_inds = [self._site_ind_id.format(*c) for c in coos]
        # physical dimension, d=2 for qubits
        dp = self.ind_size(site_inds[0])
        gate_tags = tags_to_oset(tags)

        G = qtn.tensor_1d.maybe_factor_gate_into_tensor(G, dp, numsites, coos)

        #old physical indices joined to new gate
        bond_inds = [qtn.rand_uuid() for _ in range(numsites)]
        reindex_map = dict(zip(site_inds, bond_inds))
        TG = qtn.Tensor(G,
                        inds=site_inds + bond_inds,
                        left_inds=bond_inds,
                        tags=gate_tags)

        if contract is False:
            #attach gates without contracting any bonds
            #
            #     'qA' 'qB'
            #       │   │      <- site_inds
            #       GGGGG
            #       │╱  │╱     <- bond_inds
            #     ──●───●──
            #      ╱   ╱
            #
            psi.reindex_(reindex_map)
            psi |= TG
            return psi

        elif (contract is True) or (numsites == 1):
            #
            #       │╱  │╱
            #     ──GGGGG──
            #      ╱   ╱
            #
            psi.reindex_(reindex_map)

            # get the sites that used to have the physical indices
            site_tids = psi._get_tids_from_inds(bond_inds, which='any')
            # pop the sites, contract, then re-add
            pts = [psi._pop_tensor(tid) for tid in site_tids]
            psi |= qtn.tensor_contract(*pts, TG)
            return psi

        elif contract == 'triangle_absorb' and numsites == 3:
            # absorbs 3-body gate while preserving lattice structure.
            psi.absorb_three_body_tensor_(TG=TG,
                                          coos=coos,
                                          reindex_map=reindex_map,
                                          phys_inds=site_inds,
                                          gate_tags=gate_tags,
                                          **compress_opts)
            return psi

        # NOTE: this one seems very inefficient for
        # "next-nearest" neighbor interactions.
        elif contract == 'reduce_split' and numsites == 2:
            # First absorb identity into a site, then
            # restore after gate has been applied.
            #
            # 1. Absorb identity step:
            #
            #       │     │    Absorb     │     │
            #       GGGGGGG     ident.    GGGGGGG
            #       │╱  ╱ │╱     ==>      │╱    │╱╱
            #     ──●──I──●──           ──●─────●─
            #    a ╱  ╱  ╱ b             ╱     ╱╱
            #
            # 2. Gate 'reduce_split' step:
            #
            #       │   │             │ │
            #       GGGGG             GGG               │ │
            #       │╱  │╱   ==>     ╱│ │  ╱   ==>     ╱│ │  ╱          │╱  │╱
            #     ──●───●──       ──>─●─●─<──       ──>─GGG─<──  ==>  ──G┄┄┄G──
            #      ╱   ╱           ╱     ╱           ╱     ╱           ╱   ╱
            #    <QR> <LQ>                            <SVD>
            #
            # 3. Reinsert identity:
            #
            #       │╱    │╱╱           │╱  ╱ │╱
            #     ──G┄┄┄┄┄G──   ==>   ──G┄┄I┄┄G──
            #      ╱     ╱╱            ╱  ╱  ╱
            #

            (x1, y1), (x2, y2) = coos
            mid_coo = (int((x1 + x2) / 2), int((y1 + y2) / 2))
            dummy_coo_tag = psi.site_tag_id.format(*mid_coo)

            # keep track of dummy identity's tags and neighbors
            prev_dummy_info = {
                'tags':
                psi[dummy_coo_tag].tags,
                'neighbor_tags':
                tuple(t.tags for t in psi.select_neighbors(dummy_coo_tag))
            }

            which_bond = int(
                psi.bond_size(coos[0], mid_coo) >= psi.bond_size(
                    coos[1], mid_coo))

            if which_bond == 0:  # (vertex_0 ── identity) bond is larger
                vertex_tag = psi.site_tag_id.format(*coos[0])

            else:  # (vertex_1 -- identity) bond larger
                vertex_tag = psi.site_tag_id.format(*coos[1])

            tids = psi._get_tids_from_tags(tags=(vertex_tag, dummy_coo_tag),
                                           which='any')

            # pop and reattach the (vertex & identity) tensor
            pts = [psi._pop_tensor(tid) for tid in tids]
            new_vertex = qtn.tensor_contract(*pts)

            # new_vertex.drop_tags(prev_dummy_info['tags'] - )
            new_vertex.drop_tags(pts[1].tags - pts[0].tags)

            psi |= new_vertex  # reattach [vertex & identity]

            # insert 2-body gate!
            qtn.tensor_2d.gate_string_reduce_split_(
                TG=TG,
                where=coos,
                string=coos,
                original_ts=[psi[c] for c in coos],
                bonds_along=(psi.bond(*coos), ),
                reindex_map=reindex_map,
                site_ix=site_inds,
                info=info,
                **compress_opts)

            # now restore the dummy identity between vertices
            vtensor = psi[
                coos[which_bond]]  # the vertex we absorbed dummy into

            ts_to_connect = set(
                psi[tags]
                for tags in prev_dummy_info['neighbor_tags']) - set([vtensor])

            for T2 in ts_to_connect:  # restore previous dummy bonds
                psi |= qubit_networks.insert_identity_between_tensors(
                    T1=vtensor, T2=T2, add_tags='TEMP')

            # contract new dummies into a single identity
            psi ^= 'TEMP'
            for t in prev_dummy_info['tags']:
                psi['TEMP'].add_tag(t)  # restore previous dummy tags
            psi.drop_tags('TEMP')

            return psi.fuse_multibonds_()

        elif contract == 'reduce_split_lr' and numsites == 2:
            # There will be a 'dummy' identity tensor between the
            # sites, so the 2-body operator will look "long-range"
            #
            #       │     │
            #       GGGGGGG
            #       │╱  ╱ │╱              │╱  ╱ │╱
            #     ──●──I──●──    ==>    ──G┄┄I┄┄G──
            #      ╱  ╱  ╱               ╱  ╱  ╱
            #
            (x1, y1), (x2, y2) = coos
            mid_coo = (int((x1 + x2) / 2), int((y1 + y2) / 2))

            dummy_coo_tag = psi.site_tag_id.format(*mid_coo)
            string = (coos[0], mid_coo, coos[1])

            original_ts = [psi[coo] for coo in string]
            bonds_along = [
                next(iter(qtn.bonds(t1, t2)))
                for t1, t2 in qu.utils.pairwise(original_ts)
            ]

            qtn.tensor_2d.gate_string_reduce_split_(TG=TG,
                                                    where=coos,
                                                    string=string,
                                                    original_ts=original_ts,
                                                    bonds_along=bonds_along,
                                                    reindex_map=reindex_map,
                                                    site_ix=site_inds,
                                                    info=info,
                                                    **compress_opts)

            return psi