Exemple #1
0
    def test_canonize_cyclic(self, dtype, block):
        k = MPS_rand_state(40, 10, dtype=dtype, cyclic=True)
        b = k.H
        k.add_tag('KET')
        b.add_tag('BRA')
        kb = (b | k)

        assert not np.allclose(k[block].H @ k[block], 1.0)
        assert not np.allclose(b[block].H @ b[block], 1.0)
        k.canonize_cyclic(block, bra=b)
        assert_allclose(k[block].H @ k[block], 1.0, rtol=2e-4)
        assert_allclose(b[block].H @ b[block], 1.0, rtol=2e-4)

        ii = kb.select(block, which='!any') ^ all

        if isinstance(block, slice):
            start, stop = block.start, block.stop
        else:
            start, stop = block, block + 1

        assert len(kb.select_tensors(block, 'any')) == 2 * (stop - start)

        ul, = bonds(kb[k.site_tag(start - 1), 'BRA'], kb[k.site_tag(start),
                                                         'BRA'])
        ur, = bonds(kb[k.site_tag(stop - 1), 'BRA'], kb[k.site_tag(stop),
                                                        'BRA'])
        ll, = bonds(kb[k.site_tag(start - 1), 'KET'], kb[k.site_tag(start),
                                                         'KET'])
        lr, = bonds(kb[k.site_tag(stop - 1), 'KET'], kb[k.site_tag(stop),
                                                        'KET'])

        ii = ii.to_dense((ul, ur), (ll, lr))
        assert_allclose(ii, np.eye(ii.shape[0]), rtol=0.001, atol=0.001)
Exemple #2
0
def rand_tn1d_sect(n, bd, dtype=complex):
    mps = qtn.MPS_rand_state(n + 2, bd, dtype=dtype)
    mpo = qtn.MPO_rand_herm(n + 2, 5, dtype=dtype)

    norm = qtn.TensorNetwork(qtn.align_TN_1D(mps.H, mpo, mps))

    lix = qtn.bonds(norm[0], norm[1])
    rix = qtn.bonds(norm[n], norm[n + 1])

    to = norm[1:n + 1]

    return qtn.TNLinearOperator1D(to, lix, rix, 1, n + 1)
Exemple #3
0
def rand_tn1d_sect(n, bd, dtype=complex):
    mps = qtn.MPS_rand_state(n + 2, bd, dtype=dtype)
    mpo = qtn.MPO_rand_herm(n + 2, 5, dtype=dtype)

    norm = qtn.TensorNetwork(qtn.align_TN_1D(mps.H, mpo, mps))

    # greedy not good and contracting with large bsz
    norm.structure_bsz = 2

    lix = qtn.bonds(norm[0], norm[1])
    rix = qtn.bonds(norm[n], norm[n + 1])

    to = norm[1:n + 1]

    return qtn.TNLinearOperator1D(to, lix, rix, 1, n + 1)
Exemple #4
0
    def test_TNLinearOperator1D(self):
        p = MPS_rand_state(40, 10, dtype=complex)
        pp = p.H & p
        start, stop = 10, 30
        lix = bonds(pp[start - 1], pp[start])
        rix = bonds(pp[stop - 1], pp[stop])

        sec = pp[start:stop]

        A = TNLinearOperator1D(sec, lix, rix, start, stop)
        B = sec.aslinearoperator(lix, rix)

        s1 = spla.svds(A)[1]
        s2 = spla.svds(B)[1]

        assert_allclose(s1, s2)
Exemple #5
0
def main_test():
    psi = beeky.QubitEncodeVector.rand(3, 3)
    X, Y, Z = (qu.pauli(i) for i in 'xyz')
    where = (3, 4, 9)  #which qubits to act on
    numsites = len(where)
    dp = 2  #phys ind dimension
    gate = X & X & X

    ## take over from here ##
    g_xx = qtn.tensor_1d.maybe_factor_gate_into_tensor(
        gate, dp, numsites, where)  #shape (2,2,2,2) or (2,2,2,2,2,2)

    site_inds = [psi.phys_ind_id.format(q) for q in where]
    bond_inds = [qtn.rand_uuid() for _ in range(numsites)]
    reindex_map = dict(zip(site_inds, bond_inds))

    TG = qtn.Tensor(g_xx,
                    inds=site_inds + bond_inds,
                    left_inds=bond_inds,
                    tags=['GATE'])

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

    triangle_gate_absorb(TG=TG,
                         reindex_map=reindex_map,
                         vertex_tensors=(psi[where[0]], psi[where[1]]),
                         face_tensor=psi[where[2]],
                         phys_inds=site_inds)
Exemple #6
0
 def test_cut_iter(self):
     psi = MPS_rand_state(10, 7, cyclic=True)
     pp = psi.H & psi
     bnds = bonds(pp[0], pp[-1])
     assert sum(tn ^ all for tn in pp.cut_iter(*bnds)) == pytest.approx(1.0)
     assert pp ^ all == pytest.approx(1.0)
    def absorb_three_body_tensor(self,
                                 TG,
                                 coos,
                                 reindex_map,
                                 phys_inds,
                                 gate_tags,
                                 restore_dummies=True,
                                 inplace=False,
                                 **compress_opts):
        '''Serves the same purpose as ``self.absorb_three_body_gate``, 
        but assumes gate has already been shaped into a tensor and
        appropriate indices have been gathered.

        TG: qtn.Tensor
            The 3-body gate (shape [2]*8) as a tensor.
        
        coos: sequence of tuple[int, int]
            The (x,y)-coordinates for 3 qubit sites to
            hit with the gate.
            
        phys_inds: sequence of str
            The target qubits' physical indices "k{x},{y}"
        
        reindex_map: dict[str: str]
            Map `phys_inds` to the bonds between sites and
            gate acting on those sites.
        
        gate_tags: None or sequence of str, optional
            Sites acted on with ``TG`` will have these
            tags added to them.
        
        inplace: bool
            If False, will make a copy of ``self`` and
            act on that instead.
        '''
        psi = self if inplace else self.copy()

        vertex_a, vertex_b, face_coo = coos

        face_qnum = psi.coo_to_qubit_map(face_coo)

        ## keep track of dummies' tags & neighbor tensors
        dummy_identities_info = {}

        # absorb appropriate identity tensors into vertex sites
        for k, vcoo in enumerate((vertex_a, vertex_b)):

            vertex_qnum = psi.coo_to_qubit_map(vcoo)

            # tag of identity to be absorbed
            adjacent_tag = psi.adjacent_aux_tag.format(
                vertex_qnum, face_qnum)  # "AUX{V},{F}"

            # tag of vertex to absorb identity _into_
            vertex_tag = psi._site_tag_id.format(*vcoo)

            dummy_identities_info.update({
                (k, 'neighbor_tags'):
                tuple(t.tags for t in psi.select_neighbors(adjacent_tag))
            })

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

            # pop and reattach the contracted tensors
            pts = [psi._pop_tensor(tid) for tid in tids]
            new_vertex = qtn.tensor_contract(*pts)

            dummy_identities_info.update({
                (k, 'tags'):
                pts[1].tags,  #dummy tags 
                (k, 'coo'):
                psi.find_tensor_coo(pts[1]),  #coo (x,y)
                # (k, 'inds'): pts[1].inds, #dummy indices
            })

            # drop dummy's tags from vertex site
            new_vertex.drop_tags(pts[1].tags - pts[0].tags)
            psi |= new_vertex

        vertex_tensors = [psi[coo] for coo in (vertex_a, vertex_b)]
        face_tensor = psi[face_coo]

        # apply gate!
        three_body_op.triangle_gate_absorb(TG=TG,
                                           reindex_map=reindex_map,
                                           vertex_tensors=vertex_tensors,
                                           face_tensor=face_tensor,
                                           phys_inds=phys_inds,
                                           gate_tags=gate_tags,
                                           **compress_opts)

        if not restore_dummies:
            return psi

        # now insert new dummy identities where they used to be

        # for k in range(2):
        #     vt = vertex_tensors[k]

        #     ts_to_connect = set(
        #         psi[tags] for tags in dummy_identities_info[(k, 'neighbor_tags')]
        #         ) - set([vt])

        #     for T2 in ts_to_connect:
        #         psi |= insert_identity_between_tensors(T1=vt, T2=T2, add_tags='TEMP')

        #     # contract new dummies into a single identity
        #     psi ^= 'TEMP'
        #     # restore previous tags, and drop temporary tag
        #     for tag in dummy_identities_info[(k, 'tags')]:
        #         psi['TEMP'].add_tag(tag)
        #     psi.drop_tags('TEMP')

        for k, vcoo in enumerate((vertex_a, vertex_b)):

            vtensor = psi[vcoo]
            vertex_ind = psi.site_ind_id.format(*vcoo)  # kx,y

            ts_to_connect = set(psi[tags] for tags in dummy_identities_info[
                (k, 'neighbor_tags')]) - set([vtensor])

            dummy_coo = dummy_identities_info[(k, 'coo')]  # (x,y)

            # free_inds = [ix for ix in vtensor.inds if
            #     len(psi.ind_map[ix]) == 1]

            # bonds connecting to dummy's neighbors
            dummy_bonds = qu.oset.union(*(qtn.bonds(vtensor, t)
                                          for t in ts_to_connect))

            # pop the vertex tensor, to split and reattach soon
            vtensor, = (psi._pop_tensor(x)
                        for x in psi._get_tids_from_inds(vertex_ind))

            # the dummy may not have a physical "kx,y" index
            dummy_phys_ix = psi.site_ind_id.format(*dummy_coo)
            if dummy_phys_ix in vtensor.inds:
                dummy_bonds |= qu.oset([dummy_phys_ix])

            # split into vertex & dummy
            new_vertex, new_dummy = vtensor.split(left_inds=None,
                                                  method='qr',
                                                  get='tensors',
                                                  right_inds=dummy_bonds)

            new_dummy.drop_tags()
            for t in dummy_identities_info[(k, 'tags')]:
                new_dummy.add_tag(t)

            psi |= new_vertex
            psi |= new_dummy

        return psi
    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